diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000000..60cc2ce8b0 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,76 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 88 +indent-width = 4 + +target-version = "py310" + +[lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E4", "E7", "E9", "F"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" diff --git a/docs/source/conf.py b/docs/source/conf.py index ce95592055..8d1408a894 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,14 +19,14 @@ # -- Project information ----------------------------------------------------- -project = 'Janeway' -copyright = '2018-2024 Birkbeck, University of London' -author = 'Open Library of Humanities' +project = "Janeway" +copyright = "2018-2024 Birkbeck, University of London" +author = "Open Library of Humanities" # The short X.Y version -version = '1.7.2' +version = "1.7.2" # The full version, including alpha/beta/rc tags -release = '1.7.2' +release = "1.7.2" # -- General configuration --------------------------------------------------- @@ -38,29 +38,29 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'recommonmark', - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx_rtd_theme', + "recommonmark", + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx_rtd_theme", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['ntemplates'] +templates_path = ["ntemplates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en-GB' +language = "en-GB" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -76,9 +76,9 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" html_css_files = [ - 'custom.css', + "custom.css", ] html_logo = "_static/janeway.svg" @@ -91,7 +91,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['nstatic', '_static', '_static/css/'] +html_static_path = ["nstatic", "_static", "_static/css/"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -107,7 +107,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'Janewaydoc' +htmlhelp_basename = "Janewaydoc" # -- Options for LaTeX output ------------------------------------------------ @@ -116,15 +116,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -134,8 +131,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Janeway.tex', 'Janeway Documentation', - 'Andy Byers, Mauro Sanchez \\& Martin Paul Eve', 'manual'), + ( + master_doc, + "Janeway.tex", + "Janeway Documentation", + "Andy Byers, Mauro Sanchez \\& Martin Paul Eve", + "manual", + ), ] @@ -143,10 +145,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'janeway', 'Janeway Documentation', - [author], 1) -] +man_pages = [(master_doc, "janeway", "Janeway Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -155,9 +154,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Janeway', 'Janeway Documentation', - author, 'Janeway', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "Janeway", + "Janeway Documentation", + author, + "Janeway", + "One line description of project.", + "Miscellaneous", + ), ] @@ -176,10 +181,11 @@ # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # -- Extension configuration ------------------------------------------------- + def setup(app): - app.add_css_file('_static/css/custom.css') + app.add_css_file("_static/css/custom.css") diff --git a/jenkins/janeway_settings.py b/jenkins/janeway_settings.py index 4cfdc2100f..1a08df90eb 100644 --- a/jenkins/janeway_settings.py +++ b/jenkins/janeway_settings.py @@ -1,4 +1,4 @@ -TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' +TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner" -TEST_OUTPUT_DIR = 'jenkins/test_results/' -TEST_OUTPUT_FILE_NAME = 'test_results.xml' +TEST_OUTPUT_DIR = "jenkins/test_results/" +TEST_OUTPUT_FILE_NAME = "test_results.xml" diff --git a/src/admin.py b/src/admin.py index e99aac92e8..a608f8fed6 100644 --- a/src/admin.py +++ b/src/admin.py @@ -2,5 +2,5 @@ class JanewayAdminSite(admin.AdminSite): - site_header = 'Janeway Administration' - site_title = 'Janeway Administration' + site_header = "Janeway Administration" + site_title = "Janeway Administration" diff --git a/src/api/oai/base.py b/src/api/oai/base.py index bd50a04326..a73b5319f2 100644 --- a/src/api/oai/base.py +++ b/src/api/oai/base.py @@ -17,27 +17,26 @@ metadata_formats = [ { - 'prefix': 'oai_dc', - 'schema': 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd', - 'metadataNamespace': 'http://www.openarchives.org/OAI/2.0/oai_dc/', + "prefix": "oai_dc", + "schema": "http://www.openarchives.org/OAI/2.0/oai_dc.xsd", + "metadataNamespace": "http://www.openarchives.org/OAI/2.0/oai_dc/", }, { - 'prefix': 'jats', - 'schema': 'https://jats.nlm.nih.gov/publishing/0.4/xsd/JATS-journalpublishing0.xsd', - 'metadataNamespace': 'http://jats.nlm.nih.gov', - } + "prefix": "jats", + "schema": "https://jats.nlm.nih.gov/publishing/0.4/xsd/JATS-journalpublishing0.xsd", + "metadataNamespace": "http://jats.nlm.nih.gov", + }, ] class OAIModelView(BaseListView, TemplateResponseMixin): - """ Base class for OAI views generated from model Querysets """ + """Base class for OAI views generated from model Querysets""" + content_type = "application/xml" - metadata_prefix = 'oai_dc' + metadata_prefix = "oai_dc" - metadata_formats_set = { - format.get('prefix') for format in metadata_formats - } + metadata_formats_set = {format.get("prefix") for format in metadata_formats} def get_queryset(self): qs = super().get_queryset() @@ -55,19 +54,20 @@ def apply_filters(self, qs): return qs def validate_metadata_format(self): - if self.metadata_prefix \ - and self.metadata_prefix not in self.metadata_formats_set: + if ( + self.metadata_prefix + and self.metadata_prefix not in self.metadata_formats_set + ): raise exceptions.OAIUnsupportedMetadataFormat() def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context["metadata_prefix"] = self.request.GET.get("metadataPrefix", - "oai_dc") + context["metadata_prefix"] = self.request.GET.get("metadataPrefix", "oai_dc") return context class OAIPaginationMixin(View): - """ A Mixin allowing views to be paginated via OAI resumptionToken + """A Mixin allowing views to be paginated via OAI resumptionToken The resumptionToken is a query parameter that allows a consumer of the OAI interface to resume consuming elements of a listed query when the bounds @@ -80,6 +80,7 @@ class OAIPaginationMixin(View): is addressed in the `dispatch` method where we have no option but to mutate the self.request.GET member in order to inject those querystring filters. """ + page_kwarg = "token_page" def __init__(self, *args, **kwargs): @@ -87,7 +88,7 @@ def __init__(self, *args, **kwargs): self._decoded_token = {} def dispatch(self, *args, **kwargs): - """ Adds resumptionToken encoded parameters into request.GET + """Adds resumptionToken encoded parameters into request.GET This makes the implementation of resumptionToken transparent to any child views that will see all encoded filters in the resumptionToken @@ -112,14 +113,14 @@ def get_context_data(self, **kwargs): def get_token_context(self, context): return { - "page": context["page_obj"].next_page_number(), + "page": context["page_obj"].next_page_number(), } def encode_token(self, context): token_data = {} for key, value in self.request.GET.items(): # verb is an exception as per OAI spec - if key not in {'resumptionToken', "verb"}: + if key not in {"resumptionToken", "verb"}: token_data[key] = value token_data.update(self.get_token_context(context)) @@ -141,7 +142,6 @@ def page(self): class OAIDateFilterMixin(OAIPaginationMixin): - def filter_by_date_range(self, qs): try: if self.from_: @@ -153,7 +153,10 @@ def filter_by_date_range(self, qs): # if it is not, add a default H:m:sZ. if not until_date.tzinfo: untile_date = until_date.replace( - hour=23, minute=59, second=59, tzinfo=utc, + hour=23, + minute=59, + second=59, + tzinfo=utc, ) qs = qs.filter(date_published__lte=until_date) diff --git a/src/api/oai/exceptions.py b/src/api/oai/exceptions.py index cbf53a86f0..8afef98708 100644 --- a/src/api/oai/exceptions.py +++ b/src/api/oai/exceptions.py @@ -1,8 +1,9 @@ class OAIException(Exception): - """ A Python exception that handles errors defined in the OAI spec + """A Python exception that handles errors defined in the OAI spec :attr msg: The message that will be returned from the user :attr code: A machine readable code, defined by the OAI spec """ + msg = None code = None @@ -18,15 +19,19 @@ class OAINoRecordsMatch(OAIException): class OAIDoesNotExist(OAIException): - msg = ("The value of the identifier argument is unknown or illegal" - " in this repository.") + msg = ( + "The value of the identifier argument is unknown or illegal" + " in this repository." + ) code = "idDoesNotExist" class OAIUnsupportedMetadataFormat(OAIException): - msg = ("The metadata format identified by the value given for the" - " metadataPrefix argument is not supported by the item or by" - "the repository.") + msg = ( + "The metadata format identified by the value given for the" + " metadataPrefix argument is not supported by the item or by" + "the repository." + ) code = "cannotDisseminateFormat" @@ -36,7 +41,9 @@ class OAIBadToken(OAIException): class OAIBadArgument(OAIException): - msg = ("The request includes illegal arguments, is missing required" - " arguments, includes a repeated argument, or values for arguments" - "have an illegal syntax.") + msg = ( + "The request includes illegal arguments, is missing required" + " arguments, includes a repeated argument, or values for arguments" + "have an illegal syntax." + ) code = "badArgument" diff --git a/src/api/oai/views.py b/src/api/oai/views.py index 3ce22a433c..2d29da4f2e 100644 --- a/src/api/oai/views.py +++ b/src/api/oai/views.py @@ -1,6 +1,7 @@ """ A django implementation of the OAI-PMH interface """ + from django.utils import timezone from django.views.generic.base import TemplateView @@ -15,11 +16,11 @@ # We default `verb` to ListRecords for backwards compatibility. DEFAULT_ENDPOINT = "ListRecords" -DEFAULT_METADATA_PREFIX = 'oai_dc' +DEFAULT_METADATA_PREFIX = "oai_dc" def oai_view_factory(request, *args, **kwargs): - """ Maps an incoming OAI request to a django CB view + """Maps an incoming OAI request to a django CB view The OAI protocol uses a querystring parameter (verb) to determine the resource being queried. This is not supported by Django's URL router, so we do our own mapping here and pass this factory to the @@ -42,7 +43,6 @@ def oai_view_factory(request, *args, **kwargs): class OAIListRecords(OAIPagedModelView): - # default is OAI_DC template_name = "apis/OAI_ListRecords.xml" queryset = submission_models.Article.objects.all() @@ -55,8 +55,10 @@ def get_queryset(self): queryset = queryset.filter(journal=self.request.journal) else: queryset = queryset.filter(journal__hide_from_press=False) - set_filter = self.request.GET.get('set') - issue_type_list = [issue_type.code for issue_type in journal_models.IssueType.objects.all()] + set_filter = self.request.GET.get("set") + issue_type_list = [ + issue_type.code for issue_type in journal_models.IssueType.objects.all() + ] if set_filter: filter_parts = set_filter.split(":") @@ -74,15 +76,17 @@ def get_queryset(self): # Issue/Collection try: issue = journal_models.Issue.objects.get( - journal__code=self.request.journal.code if self.request.journal else filter_parts[0], - pk=filter_parts[2] + journal__code=self.request.journal.code + if self.request.journal + else filter_parts[0], + pk=filter_parts[2], ) queryset = issue.articles.filter( date_published__isnull=False, ) except journal_models.Issue.DoesNotExist: queryset = submission_models.Article.objects.none() - elif filter_parts[1] == 'section': + elif filter_parts[1] == "section": # Section queryset = queryset.filter( section__pk=filter_parts[2], @@ -106,7 +110,9 @@ def filter_is_published(self, qs): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context["verb"] = self.request.GET.get("verb", DEFAULT_ENDPOINT) - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context @@ -118,7 +124,9 @@ def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context["article"] = self.get_article() context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) context["jats"], context["stub"] = self.get_jats(context["article"]) return context @@ -134,16 +142,14 @@ def get_jats(self, article): # check if this is a JATS XML file try: if render_galley: - with open(render_galley.file.get_file_path(article), - 'r') as galley: + with open(render_galley.file.get_file_path(article), "r") as galley: contents = galley.read() - if 'DTD JATS' in contents: + if "DTD JATS" in contents: # assume this is a JATS XML file # we need to strip the XML header, though domified_xml = minidom.parseString(contents) - return domified_xml.documentElement.toxml('utf-8'), \ - False + return domified_xml.documentElement.toxml("utf-8"), False except: # a broad catch that lets us generate a stub if anything goes wrong pass @@ -153,7 +159,7 @@ def get_jats(self, article): def get_article(self): id_param = self.request.GET.get("identifier") try: - _, site_code, id_type, identifier = id_param.split(":") + _, site_code, id_type, identifier = id_param.split(":") except (ValueError, AttributeError): raise exceptions.OAIBadArgument() @@ -165,7 +171,8 @@ def get_article(self): ) else: article = Identifier.objects.get( - id_type=id_type, identifier=identifier, + id_type=id_type, + identifier=identifier, article__stage=submission_models.STAGE_PUBLISHED, ).article except ( @@ -182,14 +189,16 @@ class OAIListIdentifiers(OAIListRecords): class OAIListMetadataFormats(TemplateView): - template_name = 'apis/OAI_ListMetadataFormats.xml' - content_type = 'application/xml' + template_name = "apis/OAI_ListMetadataFormats.xml" + content_type = "application/xml" def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context["metadata_formats"] = metadata_formats context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context @@ -199,7 +208,7 @@ class OAIIdentify(TemplateView): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context['version'] = shared.current_version() + context["version"] = shared.current_version() articles = submission_models.Article.objects.all() @@ -209,9 +218,11 @@ def get_context_data(self, *args, **kwargs): stage=submission_models.STAGE_PUBLISHED, ) - context['earliest_article'] = articles.earliest('date_published') + context["earliest_article"] = articles.earliest("date_published") context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context @@ -237,22 +248,25 @@ def get_context_data(self, *args, **kwargs): journal=self.request.journal, ) - context['journals'] = journals - context['all_issues'] = all_issues - context['sections'] = sections + context["journals"] = journals + context["all_issues"] = all_issues + context["sections"] = sections context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context class OAIErrorResponse(TemplateView): - """ Base Error response returned for raised OAI API errors + """Base Error response returned for raised OAI API errors Children should implement `error` as an instance of a OAIException. Dynamic resposne types can be generated by passing the `error` as an initkwarg to `OAIErrorResponse.as_view(**initkwargs)` as per django docs """ + template_name = "apis/OAI_error.xml" content_type = "application/xml" error = None @@ -272,7 +286,9 @@ def get_queryset(self): queryset = super().get_queryset() if self.request.repository: - queryset = queryset.filter(repository=self.request.repository).order_by('date_published') + queryset = queryset.filter(repository=self.request.repository).order_by( + "date_published" + ) return queryset def filter_is_published(self, qs): @@ -284,10 +300,13 @@ def filter_is_published(self, qs): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context["verb"] = self.request.GET.get("verb", DEFAULT_ENDPOINT) - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) context["is_preprints"] = self.request.repository return context + class OAIGetPreprintRecord(OAIGetRecord): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) @@ -300,7 +319,7 @@ def get_jats(self, article): def get_article(self): id_param = self.request.GET.get("identifier") try: - _, site_code, id_type, identifier = id_param.split(":") + _, site_code, id_type, identifier = id_param.split(":") except (ValueError, AttributeError): raise exceptions.OAIBadArgument() @@ -317,16 +336,18 @@ def get_article(self): return article + class OAIListPreprintIdentifiers(OAIListPreprintRecords): template_name = "apis/OAI_ListIdentifiers.xml" + class OAIPreprintIdentify(TemplateView): template_name = "apis/OAI_PreprintIdentify.xml" content_type = "application/xml" def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context['version'] = shared.current_version() + context["version"] = shared.current_version() articles = repo_models.Preprint.objects.all() @@ -336,12 +357,15 @@ def get_context_data(self, *args, **kwargs): stage=submission_models.STAGE_PREPRINT_PUBLISHED, ) - context['earliest_article'] = articles.earliest('date_published') + context["earliest_article"] = articles.earliest("date_published") context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context + class OAIListPreprintSets(TemplateView): template_name = "apis/OAI_ListSets.xml" content_type = "application/xml" @@ -350,10 +374,13 @@ def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context["verb"] = self.request.GET.get("verb") - context["metadataPrefix"] = self.request.GET.get("metadataPrefix", DEFAULT_METADATA_PREFIX) + context["metadataPrefix"] = self.request.GET.get( + "metadataPrefix", DEFAULT_METADATA_PREFIX + ) return context + PREPRINT_ROUTES = { "GetRecord": OAIGetPreprintRecord.as_view(), "ListRecords": OAIListPreprintRecords.as_view(), diff --git a/src/api/permissions.py b/src/api/permissions.py index 5493965533..7afeeed088 100755 --- a/src/api/permissions.py +++ b/src/api/permissions.py @@ -4,7 +4,7 @@ class IsEditor(permissions.BasePermission): - message = 'Please ensure the user is an Editor or Staff Member.' + message = "Please ensure the user is an Editor or Staff Member." def has_permission(self, request, view): if request.user and not request.user.is_authenticated: @@ -18,7 +18,7 @@ def has_permission(self, request, view): class IsSectionEditor(permissions.BasePermission): - message = 'Please ensure the user is a Section Editor or Staff Member.' + message = "Please ensure the user is a Section Editor or Staff Member." def has_permission(self, request, view): if request.user and not request.user.is_authenticated: diff --git a/src/api/serializers.py b/src/api/serializers.py index aa45a7a54e..db6804a613 100755 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -7,24 +7,29 @@ class LicenceSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = submission_models.Licence - fields = ('name', 'short_name', 'text', 'url') + fields = ("name", "short_name", "text", "url") class KeywordsSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = submission_models.Keyword - fields = ('word',) + fields = ("word",) class FrozenAuthorSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = submission_models.FrozenAuthor - fields = ('first_name', 'middle_name', 'last_name', 'name_suffix', 'institution', 'department', 'country') + fields = ( + "first_name", + "middle_name", + "last_name", + "name_suffix", + "institution", + "department", + "country", + ) country = serializers.ReadOnlyField( read_only=True, @@ -33,64 +38,89 @@ class Meta: class GalleySerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = core_models.Galley - fields = ('label', 'type', 'path') + fields = ("label", "type", "path") class ArticleSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = submission_models.Article - fields = ('pk', 'title', 'subtitle', 'abstract', 'language', 'license', 'keywords', 'section', - 'is_remote', 'remote_url', 'frozenauthors', 'date_submitted', 'date_accepted', - 'date_published', 'render_galley', 'galleys') + fields = ( + "pk", + "title", + "subtitle", + "abstract", + "language", + "license", + "keywords", + "section", + "is_remote", + "remote_url", + "frozenauthors", + "date_submitted", + "date_accepted", + "date_published", + "render_galley", + "galleys", + ) license = LicenceSerializer() keywords = KeywordsSerializer( many=True, read_only=True, ) - section = serializers.ReadOnlyField( - read_only=True, - source='section.name' - ) + section = serializers.ReadOnlyField(read_only=True, source="section.name") frozenauthors = FrozenAuthorSerializer( many=True, - source='frozenauthor_set', + source="frozenauthor_set", ) render_galley = GalleySerializer( read_only=True, ) - galleys = GalleySerializer( - source='galley_set', - many=True - ) + galleys = GalleySerializer(source="galley_set", many=True) -class PreprintSubjectSerializer(serializers.HyperlinkedModelSerializer): +class PreprintSubjectSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = repository_models.Subject - fields = ('name',) + fields = ("name",) -class PreprintFileSerializer(serializers.ModelSerializer): +class PreprintFileSerializer(serializers.ModelSerializer): class Meta: model = repository_models.PreprintFile - fields = ('original_filename', 'mime_type', 'download_url',) + fields = ( + "original_filename", + "mime_type", + "download_url", + ) -class PreprintSupplementaryFileSerializer(serializers.ModelSerializer): +class PreprintSupplementaryFileSerializer(serializers.ModelSerializer): class Meta: model = repository_models.PreprintSupplementaryFile - fields = ('url', 'label',) -class IssueSerializer(serializers.HyperlinkedModelSerializer): + fields = ( + "url", + "label", + ) + +class IssueSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = journal_models.Issue - fields = ('pk', 'volume', 'issue', 'issue_title', 'date', 'issue_type', 'issue_description', - 'cover_image', 'large_image', 'articles') + fields = ( + "pk", + "volume", + "issue", + "issue_title", + "date", + "issue_type", + "issue_description", + "cover_image", + "large_image", + "articles", + ) issue_type = serializers.ReadOnlyField( read_only=True, @@ -99,79 +129,115 @@ class Meta: class JournalSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = journal_models.Journal - fields = ('pk', 'code', 'name', 'publisher', 'issn', 'description', 'current_issue', 'default_cover_image', - 'default_large_image', 'issues') + fields = ( + "pk", + "code", + "name", + "publisher", + "issn", + "description", + "current_issue", + "default_cover_image", + "default_large_image", + "issues", + ) issues = serializers.HyperlinkedRelatedField( many=True, - source='issue_set', + source="issue_set", read_only=True, - view_name='issue-detail', + view_name="issue-detail", ) class RoleSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = core_models.Role - fields = ('pk', 'slug',) + fields = ( + "pk", + "slug", + ) class AccountSerializer(serializers.HyperlinkedModelSerializer): - class Meta: model = core_models.Account fields = ( - 'pk', 'email', 'first_name', 'middle_name', 'last_name', - 'salutation', 'orcid', 'is_active', + "pk", + "email", + "first_name", + "middle_name", + "last_name", + "salutation", + "orcid", + "is_active", ) -class PreprintAccountSerializer(serializers.HyperlinkedModelSerializer): +class PreprintAccountSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = core_models.Account fields = ( - 'pk', 'first_name', 'middle_name', 'last_name', - 'salutation', 'orcid', + "pk", + "first_name", + "middle_name", + "last_name", + "salutation", + "orcid", ) -class AccountRoleSerializer(serializers.ModelSerializer): +class AccountRoleSerializer(serializers.ModelSerializer): class Meta: model = core_models.AccountRole - fields = ('pk', 'journal', 'user', 'role') + fields = ("pk", "journal", "user", "role") def validate(self, data): - request = self.context.get('request', None) + request = self.context.get("request", None) role = data.get("role") - excluded_roles = ['reader'] + excluded_roles = ["reader"] # if the current user is not staff add the journal-manager role to # the list of excluded roles. if not request or not request.user.is_staff: - excluded_roles.append('journal-manager') + excluded_roles.append("journal-manager") if role.slug in excluded_roles: - raise serializers.ValidationError({"error": "You cannot add a user to that role via the API."}) + raise serializers.ValidationError( + {"error": "You cannot add a user to that role via the API."} + ) return data + class RepositoryFieldAnswerSerializer(serializers.ModelSerializer): class Meta: model = repository_models.RepositoryFieldAnswer - fields = ['pk', 'answer'] + fields = ["pk", "answer"] -class PreprintSerializer(serializers.ModelSerializer): +class PreprintSerializer(serializers.ModelSerializer): class Meta: model = repository_models.Preprint - fields = ('pk', 'title', 'abstract', 'license', 'keywords', - 'date_submitted', 'date_accepted', 'date_published', - 'doi', 'preprint_doi', 'authors', 'subject', 'files', 'supplementary_files') + fields = ( + "pk", + "title", + "abstract", + "license", + "keywords", + "date_submitted", + "date_accepted", + "date_published", + "doi", + "preprint_doi", + "authors", + "subject", + "files", + "supplementary_files", + ) depth = 2 authors = PreprintAccountSerializer( diff --git a/src/api/tests/test_api.py b/src/api/tests/test_api.py index b0c6c99b2e..876c562b46 100644 --- a/src/api/tests/test_api.py +++ b/src/api/tests/test_api.py @@ -8,32 +8,31 @@ class TestAPI(TestCase): - @classmethod def setUpTestData(cls): cls.press = helpers.create_press() cls.journal, _ = helpers.create_journals() cls.staff_member = helpers.create_user( - username='t.paris@voyager.com', - roles=['author'], + username="t.paris@voyager.com", + roles=["author"], journal=cls.journal, is_staff=True, is_active=True, ) cls.editor = helpers.create_user( - username='h.kim@voyager.com', - roles=['editor'], + username="h.kim@voyager.com", + roles=["editor"], journal=cls.journal, is_active=True, ) cls.average_user = helpers.create_user( - 'a.redshirt@voyager.com', - roles=['author'], + "a.redshirt@voyager.com", + roles=["author"], journal=cls.journal, is_active=True, ) helpers.create_roles( - ['journal-manager'], + ["journal-manager"], ) cls.api_client = APIClient() @@ -42,18 +41,18 @@ def test_staff_member_can_assign_journal_manager_role(self): self.api_client.force_authenticate(user=self.staff_member) url = self.journal.site_url( reverse( - 'accountrole-list', + "accountrole-list", ) ) journal_manager_role = core_models.Role.objects.get( - slug='journal-manager', + slug="journal-manager", ) self.api_client.post( path=url, data={ - 'user': self.average_user.pk, - 'role': journal_manager_role.pk, - 'journal': self.journal.pk, + "user": self.average_user.pk, + "role": journal_manager_role.pk, + "journal": self.journal.pk, }, SERVER_NAME=self.journal.domain, ) @@ -69,18 +68,18 @@ def test_editor_cannot_assign_journal_manager_role(self): self.api_client.force_authenticate(user=self.editor) url = self.journal.site_url( reverse( - 'accountrole-list', + "accountrole-list", ) ) journal_manager_role = core_models.Role.objects.get( - slug='journal-manager', + slug="journal-manager", ) self.api_client.post( path=url, data={ - 'user': self.average_user.pk, - 'role': journal_manager_role.pk, - 'journal': self.journal.pk, + "user": self.average_user.pk, + "role": journal_manager_role.pk, + "journal": self.journal.pk, }, SERVER_NAME=self.journal.domain, ) @@ -89,4 +88,4 @@ def test_editor_cannot_assign_journal_manager_role(self): self.journal, journal_manager_role, ) - ) \ No newline at end of file + ) diff --git a/src/api/tests/test_oai.py b/src/api/tests/test_oai.py index 642de3f746..701ddefb07 100644 --- a/src/api/tests/test_oai.py +++ b/src/api/tests/test_oai.py @@ -22,7 +22,6 @@ class TestOAIViews(TestCase): - @classmethod def setUpTestData(cls): cls.press = helpers.create_press() @@ -36,11 +35,15 @@ def setUpTestData(cls): authors=[cls.author], ) cls.issue = helpers.create_issue( - journal=cls.journal, vol=1, number=1, + journal=cls.journal, + vol=1, + number=1, articles=[cls.article], ) - cls.issue_2= helpers.create_issue( - journal=cls.journal, vol=1, number=2, + cls.issue_2 = helpers.create_issue( + journal=cls.journal, + vol=1, + number=2, articles=[cls.article], ) cls.article.primary_issue = cls.issue @@ -48,9 +51,7 @@ def setUpTestData(cls): @classmethod def validate_oai_schema(cls, xml): - xml_schema = etree.XMLSchema( - "http://www.openarchives.org/OAI/2.0/oai_dc.xsd" - ) + xml_schema = etree.XMLSchema("http://www.openarchives.org/OAI/2.0/oai_dc.xsd") xml_dom = etree.XML(xml) return xml_schema.validate(xml_dom) @@ -58,23 +59,22 @@ def validate_oai_schema(cls, xml): @freeze_time(FROZEN_DATETIME_2012) def test_list_records_dc(self): expected = LIST_RECORDS_DATA_DC - response = self.client.get(reverse('OAI_list_records'), SERVER_NAME="testserver") + response = self.client.get( + reverse("OAI_list_records"), SERVER_NAME="testserver" + ) self.assertEqual(str(response.rendered_content).split(), expected.split()) @override_settings(URL_CONFIG="domain") @freeze_time(FROZEN_DATETIME_2012) def test_list_records_jats(self): expected = LIST_RECORDS_DATA_JATS - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListRecords", metadataPrefix="jats", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") result = str(response.rendered_content) self.assertEqual(result.split(), expected.split()) @@ -83,7 +83,7 @@ def test_list_records_jats(self): def test_get_record_dc(self): expected = GET_RECORD_DATA_DC - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="GetRecord", metadataPrefix="oai_dc", @@ -91,10 +91,7 @@ def test_get_record_dc(self): ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -103,7 +100,7 @@ def test_get_record_dc(self): def test_get_records_until(self): expected = GET_RECORD_DATA_UNTIL - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListRecords", metadataPrefix="oai_dc", @@ -126,10 +123,7 @@ def test_get_records_until(self): date_published="1977-01-01T17:00:00.000+0200", authors=[self.author], ) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -142,8 +136,7 @@ def test_get_record_jats(self): self.article.authors.add(author_2) self.article.snapshot_authors() - - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="GetRecord", metadataPrefix="jats", @@ -151,10 +144,7 @@ def test_get_record_jats(self): ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.maxDiff = None self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -164,17 +154,14 @@ def test_get_record_jats(self): def test_list_identifiers_jats(self): expected = LIST_IDENTIFIERS_JATS - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListIdentifiers", metadataPrefix="jats", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -183,17 +170,14 @@ def test_list_identifiers_jats(self): def test_identify_dc(self): expected = IDENTIFY_DATA_DC - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="Identify", metadataPrefix="oai_dc", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.assertEqual(str(response.rendered_content).split(), expected.split()) @override_settings(URL_CONFIG="domain") @@ -201,7 +185,7 @@ def test_identify_dc(self): def test_list_sets(self): section = sm_models.Section.objects.get( journal__code=self.journal.code, - name='Article', + name="Article", ) expected = LIST_SETS_DATA_DC.format( journal_code=self.journal.code, @@ -214,17 +198,14 @@ def test_list_sets(self): issue_two_title=self.issue_2.non_pretty_issue_identifier, ) - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListSets", metadataPrefix="oai_dc", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") result = str(response.rendered_content) self.assertEqual(result.split(), expected.split()) @@ -233,6 +214,7 @@ def test_list_sets(self): def test_oai_resumption_token_decode(self): expected = {"custom-param": "custom-value"} encoded = {"resumptionToken": urlencode(expected)} + class TestView(OAIPaginationMixin): pass @@ -243,7 +225,8 @@ class TestView(OAIPaginationMixin): request = RequestFactory().get("/api/oai", query_params) TestView.as_view()(request) self.assertEqual( - request.GET.get("custom-param"), expected["custom-param"], + request.GET.get("custom-param"), + expected["custom-param"], "Parameters not being encoded by resumptionToken correctly", ) @@ -265,17 +248,14 @@ def test_oai_resumption_token_encode(self): authors=[self.author], ) - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListRecords", metadataPrefix="jats", **custom_param, ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="testserver" - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME="testserver") self.assertTrue( expected_encoded in unquote_plus(response.context["resumption_token"]), "Query parameter has not been encoded into resumption_token", diff --git a/src/api/tests/test_preprint_oai.py b/src/api/tests/test_preprint_oai.py index e08de65d6d..5b0570fa77 100644 --- a/src/api/tests/test_preprint_oai.py +++ b/src/api/tests/test_preprint_oai.py @@ -1,4 +1,4 @@ -#from urllib.parse import unquote_plus +# from urllib.parse import unquote_plus from django.test import TestCase, override_settings from django.urls import reverse from django.utils.http import urlencode @@ -9,12 +9,12 @@ from utils.testing import helpers from repository import models as repo_models -REPO_DOMAIN = 'repo.domain.com' +REPO_DOMAIN = "repo.domain.com" FROZEN_DATETIME_202209 = timezone.make_aware(timezone.datetime(2022, 9, 1, 0, 0, 0)) FROZEN_DATETIME_202208 = timezone.make_aware(timezone.datetime(2022, 8, 31, 0, 0, 0)) -class TestPreprintOAIViews(TestCase): +class TestPreprintOAIViews(TestCase): @classmethod @freeze_time(FROZEN_DATETIME_202208) def setUpTestData(cls): @@ -28,15 +28,23 @@ def setUpTestData(cls): cls.preprint = helpers.create_preprint(cls.repo, cls.author, cls.subject) cls.preprint.stage = repo_models.STAGE_PREPRINT_PUBLISHED - cls.preprint.date_published = timezone.make_aware(timezone.datetime(2022, 8, 31, 0, 0, 0)) + cls.preprint.date_published = timezone.make_aware( + timezone.datetime(2022, 8, 31, 0, 0, 0) + ) cls.preprint.make_new_version(cls.preprint.submission_file) cls.preprint.save() - cls.unpublished_preprint = helpers.create_preprint(cls.repo, cls.author, cls.subject, title="Unpublished Preprint") + cls.unpublished_preprint = helpers.create_preprint( + cls.repo, cls.author, cls.subject, title="Unpublished Preprint" + ) - cls.older_preprint = helpers.create_preprint(cls.repo, cls.author, cls.subject, title="Older Test Preprint") + cls.older_preprint = helpers.create_preprint( + cls.repo, cls.author, cls.subject, title="Older Test Preprint" + ) cls.older_preprint.stage = repo_models.STAGE_PREPRINT_PUBLISHED - cls.older_preprint.date_published = timezone.make_aware(timezone.datetime(2022, 8, 29, 0, 0, 0)) + cls.older_preprint.date_published = timezone.make_aware( + timezone.datetime(2022, 8, 29, 0, 0, 0) + ) cls.older_preprint.make_new_version(cls.older_preprint.submission_file) cls.older_preprint.save() @@ -44,23 +52,20 @@ def setUpTestData(cls): @freeze_time(FROZEN_DATETIME_202209) def test_list_records_dc(self): expected = LIST_RECORDS_DATA_DC - response = self.client.get(reverse('OAI_list_records'), SERVER_NAME=REPO_DOMAIN) + response = self.client.get(reverse("OAI_list_records"), SERVER_NAME=REPO_DOMAIN) self.assertEqual(str(response.rendered_content).split(), expected.split()) @override_settings(URL_CONFIG="domain") @freeze_time(FROZEN_DATETIME_202209) def test_list_records_jats(self): expected = LIST_RECORDS_DATA_JATS - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListRecords", metadataPrefix="jats", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME=REPO_DOMAIN - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME=REPO_DOMAIN) self.assertEqual(str(response.rendered_content).split(), expected.split()) @override_settings(URL_CONFIG="domain") @@ -68,27 +73,23 @@ def test_list_records_jats(self): def test_list_sets(self): expected = LIST_SETS_DATA_DC - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListSets", metadataPrefix="oai_dc", ) query_string = urlencode(query_params) - response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME=REPO_DOMAIN - ) + response = self.client.get(f"{path}?{query_string}", SERVER_NAME=REPO_DOMAIN) self.assertEqual(str(response.rendered_content).split(), expected.split()) - @override_settings(URL_CONFIG="domain") @freeze_time(FROZEN_DATETIME_202209) def test_get_record_dc(self): expected = GET_RECORD_DATA_DC - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="GetRecord", metadataPrefix="oai_dc", @@ -97,8 +98,7 @@ def test_get_record_dc(self): query_string = urlencode(query_params) response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="repo.domain.com" + f"{path}?{query_string}", SERVER_NAME="repo.domain.com" ) self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -108,7 +108,7 @@ def test_get_record_dc(self): def test_get_record_jats(self): expected = GET_RECORD_DATA_JATS - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="GetRecord", metadataPrefix="jats", @@ -117,8 +117,7 @@ def test_get_record_jats(self): query_string = urlencode(query_params) response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="repo.domain.com" + f"{path}?{query_string}", SERVER_NAME="repo.domain.com" ) self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -128,7 +127,7 @@ def test_get_record_jats(self): def test_list_identifiers_jats(self): expected = LIST_IDENTIFIERS_JATS - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListIdentifiers", metadataPrefix="jats", @@ -136,8 +135,7 @@ def test_list_identifiers_jats(self): query_string = urlencode(query_params) response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="repo.domain.com" + f"{path}?{query_string}", SERVER_NAME="repo.domain.com" ) self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -147,7 +145,7 @@ def test_list_identifiers_jats(self): def test_identify_dc(self): expected = IDENTIFY_DATA_DC - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="Identify", metadataPrefix="oai_dc", @@ -155,8 +153,7 @@ def test_identify_dc(self): query_string = urlencode(query_params) response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="repo.domain.com" + f"{path}?{query_string}", SERVER_NAME="repo.domain.com" ) self.assertEqual(str(response.rendered_content).split(), expected.split()) @@ -165,22 +162,22 @@ def test_identify_dc(self): def test_get_records_until(self): expected = GET_RECORD_DATA_UNTIL - path = reverse('OAI_list_records') + path = reverse("OAI_list_records") query_params = dict( verb="ListRecords", metadataPrefix="oai_dc", # until=str(datetime.datetime(2022, 8, 30, tzinfo=pytz.UTC)), - until=timezone.make_aware(timezone.datetime(2022, 8, 30, 0, 0, 0)) + until=timezone.make_aware(timezone.datetime(2022, 8, 30, 0, 0, 0)), ) query_string = urlencode(query_params) response = self.client.get( - f'{path}?{query_string}', - SERVER_NAME="repo.domain.com" + f"{path}?{query_string}", SERVER_NAME="repo.domain.com" ) self.assertEqual(str(response.rendered_content).split(), expected.split()) + LIST_RECORDS_DATA_DC = """ len(original_part.get_text().strip()): @@ -159,15 +156,15 @@ def add_searchable_page_parts(docs, url, body, headings=None): :rtype: list[dict] """ if not headings: - headings = ['h4', 'h3', 'h2'] + headings = ["h4", "h3", "h2"] for h in headings: for part in body.find_all(h, id=True): if not part: continue - part_id = part.get('id', '') + part_id = part.get("id", "") if part_id: - part_url = urljoin(url, '#' + part_id) + part_url = urljoin(url, "#" + part_id) else: part_url = remove_fragment(url) part_name = part.get_text().strip() @@ -185,7 +182,7 @@ def remove_fragment(url): :rtype: str """ fragment = urlparse(url).fragment - return url.replace('#' + fragment, '') + return url.replace("#" + fragment, "") def normalize_url(url): @@ -195,7 +192,7 @@ def normalize_url(url): :type url: str :rtype: str """ - return remove_fragment(url).rstrip('/') + return remove_fragment(url).rstrip("/") def get_page(url, fetched_urls): @@ -206,26 +203,24 @@ def get_page(url, fetched_urls): :type fetched_urls: set[str] :rtype: tuple[BeautifulSoup | None, set[str]] """ - time.sleep(.1) + time.sleep(0.1) try: fetched_urls.add(normalize_url(url)) - headers = { - 'Accept': 'text/html; charset=utf-8' - } + headers = {"Accept": "text/html; charset=utf-8"} response = requests.get(url, headers=headers) except requests.exceptions.ConnectionError: - logger.warn(f'Could not access {url}') - logger.warn('Please run server to index site search') + logger.warn(f"Could not access {url}") + logger.warn("Please run server to index site search") return None, fetched_urls if response.status_code != 200: - logger.warn(f'Could not access {url}') + logger.warn(f"Could not access {url}") return None, fetched_urls - if 'text/html' not in response.headers['Content-Type']: + if "text/html" not in response.headers["Content-Type"]: return None, fetched_urls - soup = BeautifulSoup(response.text, 'html.parser') + soup = BeautifulSoup(response.text, "html.parser") return soup, fetched_urls @@ -237,7 +232,7 @@ def get_name(html): :rtype: str """ try: - return html.find('h1').get_text().strip() + return html.find("h1").get_text().strip() except AttributeError: return html.title.get_text().strip() @@ -250,10 +245,10 @@ def get_body(html): :type html: BeautifulSoup :rtype: Tag | None """ - body = html.find('body') + body = html.find("body") if not isinstance(body, Tag): return - for non_content_selector in ['script', '.djdt-hidden']: + for non_content_selector in ["script", ".djdt-hidden"]: for element in body.select(non_content_selector): element.decompose() for element in body.find_all(text=lambda text: isinstance(text, Comment)): @@ -280,9 +275,7 @@ def excluded_urls(): :rtype: list[str] """ - return [ - journal.site_url() for journal in journal_models.Journal.objects.all() - ] + [ + return [journal.site_url() for journal in journal_models.Journal.objects.all()] + [ repo.site_url() for repo in repository_models.Repository.objects.all() ] @@ -326,7 +319,7 @@ def decompose_non_content_page_regions(body): :type body: Tag :rtype: None """ - for non_content_selector in ['header', 'footer', 'h1']: + for non_content_selector in ["header", "footer", "h1"]: for element in body.select(non_content_selector): element.decompose() @@ -350,15 +343,12 @@ def add_searchable_page(press, docs, fetched_urls, url): if not body: return docs, fetched_urls - for anchor in body.find_all('a'): - href = anchor.get('href', '').strip() + for anchor in body.find_all("a"): + href = anchor.get("href", "").strip() deeper_url = urljoin(url, href) if url_in_scope(press, deeper_url) and url_is_new(fetched_urls, deeper_url): docs, fetched_urls = add_searchable_page( - press, - docs, - fetched_urls, - deeper_url + press, docs, fetched_urls, deeper_url ) name = get_name(html) @@ -385,37 +375,29 @@ def get_press_site_search_data(press): docs, fetched_urls = add_searchable_page(press, docs, fetched_urls, base) if not len(docs) > 0: - logger.error('Search data store is empty') + logger.error("Search data store is empty") return docs def get_search_docs_filename(press): - return os.path.join( - settings.SITE_SEARCH_DIR, - f'_press_{ press.pk }_documents.json' - ) + return os.path.join(settings.SITE_SEARCH_DIR, f"_press_{ press.pk }_documents.json") def get_search_docs_filepath(press): - return os.path.join( - SITE_SEARCH_PATH, - f'_press_{ press.pk }_documents.json' - ) + return os.path.join(SITE_SEARCH_PATH, f"_press_{ press.pk }_documents.json") def update_search_data(press): docs_filename = get_search_docs_filename(press) - docs_file, created = models.MediaFile.objects.get_or_create( - label=docs_filename - ) + docs_file, created = models.MediaFile.objects.get_or_create(label=docs_filename) if not created: docs_file.unlink() documents = get_press_site_search_data(press) - docs_json = json.dumps(documents, separators=(',', ':')) + docs_json = json.dumps(documents, separators=(",", ":")) - content_file = ContentFile(docs_json.encode('utf-8')) + content_file = ContentFile(docs_json.encode("utf-8")) docs_file.file.save(docs_filename, content_file, save=True) return docs_file @@ -428,9 +410,7 @@ def delete_search_data(press): files_deleted.append(path) if settings.IN_TEST_RUNNER: if os.listdir(SITE_SEARCH_PATH): - logger.warning( - f'Left-over test files: {os.listdir(SITE_SEARCH_PATH)}' - ) + logger.warning(f"Left-over test files: {os.listdir(SITE_SEARCH_PATH)}") else: os.rmdir(SITE_SEARCH_PATH) @@ -444,50 +424,49 @@ def get_search_data_url(press): return models.MediaFile.objects.get(label=docs_filename).file.url except models.MediaFile.DoesNotExist: raise ImproperlyConfigured( - 'Site search indexing is turned on, but there is no data file. ' - 'Set settings.SITE_SEARCH_INDEXING_FREQUENCY to None to turn off, ' - 'or run manage.py generate_site_search_data.' + "Site search indexing is turned on, but there is no data file. " + "Set settings.SITE_SEARCH_INDEXING_FREQUENCY to None to turn off, " + "or run manage.py generate_site_search_data." ) def get_custom_templates_folder(journal): setting = setting_handler.get_setting( - 'general', - 'custom_cms_templates', + "general", + "custom_cms_templates", journal, default=False, ) - return setting.processed_value if setting else '' + return setting.processed_value if setting else "" def get_custom_templates_path(journal, press): if journal: theme = setting_handler.get_setting( - 'general', - 'journal_theme', + "general", + "journal_theme", journal, ).processed_value elif press and press.theme: theme = press.theme else: - return '' + return "" folder = get_custom_templates_folder(journal) if not folder: - return '' + return "" - return os.path.join(settings.BASE_DIR, 'themes', theme, 'templates', folder) + return os.path.join(settings.BASE_DIR, "themes", theme, "templates", folder) def get_custom_templates(journal, press): - templates_folder = get_custom_templates_folder(journal) templates_path = get_custom_templates_path(journal, press) if not templates_folder or not templates_path: return [] - custom_templates = [('','-----')] - for filepath in sorted(glob.glob(os.path.join(templates_path, '*.html'))): + custom_templates = [("", "-----")] + for filepath in sorted(glob.glob(os.path.join(templates_path, "*.html"))): choice = ( os.path.join(templates_folder, os.path.basename(filepath)), os.path.basename(filepath), diff --git a/src/cms/management/commands/delete_site_search_data.py b/src/cms/management/commands/delete_site_search_data.py index 7657c5ef8a..16d86d11e0 100644 --- a/src/cms/management/commands/delete_site_search_data.py +++ b/src/cms/management/commands/delete_site_search_data.py @@ -8,27 +8,21 @@ class Command(BaseCommand): - help = 'Deletes files used by MiniSearch site search' + help = "Deletes files used by MiniSearch site search" def add_arguments(self, parser): - parser.add_argument('--press_id', type=int) + parser.add_argument("--press_id", type=int) def handle(self, *args, **options): - if options['press_id']: - press = press_models.Press.objects.get(pk=options['press_id']) + if options["press_id"]: + press = press_models.Press.objects.get(pk=options["press_id"]) else: press = press_models.Press.objects.first() files_deleted = cms_logic.delete_search_data(press) if files_deleted: logger.debug( - self.style.SUCCESS( - 'Deleted files: ' + ', '.join(files_deleted) - ) + self.style.SUCCESS("Deleted files: " + ", ".join(files_deleted)) ) else: - logger.warning( - self.style.WARNING( - 'No search data files found' - ) - ) + logger.warning(self.style.WARNING("No search data files found")) diff --git a/src/cms/management/commands/generate_site_search_data.py b/src/cms/management/commands/generate_site_search_data.py index 01ab4febac..6f849bcfa0 100644 --- a/src/cms/management/commands/generate_site_search_data.py +++ b/src/cms/management/commands/generate_site_search_data.py @@ -8,19 +8,15 @@ class Command(BaseCommand): - help = 'Generates new site search data and saves it as a MediaFile' + help = "Generates new site search data and saves it as a MediaFile" def add_arguments(self, parser): - parser.add_argument('--press_id', type=int) + parser.add_argument("--press_id", type=int) def handle(self, *args, **options): - if options['press_id']: - press = press_models.Press.objects.get(pk=options['press_id']) + if options["press_id"]: + press = press_models.Press.objects.get(pk=options["press_id"]) else: press = press_models.Press.objects.first() documents = cms_logic.update_search_data(press) - logger.debug( - self.style.SUCCESS( - f'Successfully updated {documents}' - ) - ) + logger.debug(self.style.SUCCESS(f"Successfully updated {documents}")) diff --git a/src/cms/migrations/0001_initial.py b/src/cms/migrations/0001_initial.py index 4097a026da..f64912ec5e 100755 --- a/src/cms/migrations/0001_initial.py +++ b/src/cms/migrations/0001_initial.py @@ -7,48 +7,136 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.CreateModel( - name='NavigationItem', + name="NavigationItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('link_name', models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, verbose_name='Display name')), - ('link', models.CharField(max_length=100)), - ('is_external', models.BooleanField(default=False, help_text='Whether the link is to an external website.')), - ('sequence', models.IntegerField(default=99, help_text='The order in which custom nav items appear relative to other custom nav items. Note that fixed (default) nav items do not have a sequence field, so you have to replace them with custom elements to change their order.')), - ('has_sub_nav', models.BooleanField(default=False, help_text='Whether this item has sub-nav items under it.', verbose_name='Has sub navigation')), - ('content_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='nav_content', to='contenttypes.ContentType')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "link_name", + models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + verbose_name="Display name", + ), + ), + ("link", models.CharField(max_length=100)), + ( + "is_external", + models.BooleanField( + default=False, + help_text="Whether the link is to an external website.", + ), + ), + ( + "sequence", + models.IntegerField( + default=99, + help_text="The order in which custom nav items appear relative to other custom nav items. Note that fixed (default) nav items do not have a sequence field, so you have to replace them with custom elements to change their order.", + ), + ), + ( + "has_sub_nav", + models.BooleanField( + default=False, + help_text="Whether this item has sub-nav items under it.", + verbose_name="Has sub navigation", + ), + ), + ( + "content_type", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="nav_content", + to="contenttypes.ContentType", + ), + ), ], ), migrations.CreateModel( - name='Page', + name="Page", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('name', models.CharField(help_text='Page name displayed in the URL bar eg. about or contact', max_length=300)), - ('display_name', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100)), - ('content', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('is_markdown', models.BooleanField(default=True)), - ('edited', models.DateTimeField(auto_now=True)), - ('content_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='page_content', to='contenttypes.ContentType')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "name", + models.CharField( + help_text="Page name displayed in the URL bar eg. about or contact", + max_length=300, + ), + ), + ( + "display_name", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + ), + ), + ( + "content", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ("is_markdown", models.BooleanField(default=True)), + ("edited", models.DateTimeField(auto_now=True)), + ( + "content_type", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="page_content", + to="contenttypes.ContentType", + ), + ), ], ), migrations.AddField( - model_name='navigationitem', - name='page', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cms.Page'), + model_name="navigationitem", + name="page", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="cms.Page", + ), ), migrations.AddField( - model_name='navigationitem', - name='top_level_nav', - field=models.ForeignKey(blank=True, help_text='If this is a sub-nav item, which top-level item should it go under?', null=True, on_delete=django.db.models.deletion.CASCADE, to='cms.NavigationItem', verbose_name='Top-level nav item'), + model_name="navigationitem", + name="top_level_nav", + field=models.ForeignKey( + blank=True, + help_text="If this is a sub-nav item, which top-level item should it go under?", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="cms.NavigationItem", + verbose_name="Top-level nav item", + ), ), ] diff --git a/src/cms/migrations/0002_migrate_issue_types.py b/src/cms/migrations/0002_migrate_issue_types.py index aa247be176..af910bb024 100644 --- a/src/cms/migrations/0002_migrate_issue_types.py +++ b/src/cms/migrations/0002_migrate_issue_types.py @@ -9,15 +9,14 @@ def migrate_collections_navigation(apps, schema_editor): Journal = apps.get_model("journal", "Journal") IssueType = apps.get_model("journal", "IssueType") NavigationItem = apps.get_model("cms", "NavigationItem") - ContentType = apps.get_model('contenttypes', 'ContentType') + ContentType = apps.get_model("contenttypes", "ContentType") for journal in Journal.objects.all(): if journal.nav_collections: issue_type = IssueType.objects.get( journal=journal, code="collection", ) - content_type = ContentType.objects.get( - app_label="journal", model="Journal") + content_type = ContentType.objects.get(app_label="journal", model="Journal") defaults = { "link_name": issue_type.pretty_name + "s", "link": "/collections/%s" % (issue_type.code), @@ -30,15 +29,13 @@ def migrate_collections_navigation(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0034_migrate_issue_types'), - ('cms', '0001_initial'), + ("journal", "0034_migrate_issue_types"), + ("cms", "0001_initial"), ] operations = [ migrations.RunPython( - migrate_collections_navigation, - reverse_code=migrations.RunPython.noop + migrate_collections_navigation, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/cms/migrations/0003_auto_20210510_1148.py b/src/cms/migrations/0003_auto_20210510_1148.py index a191cd4fbc..9398b5f7f0 100644 --- a/src/cms/migrations/0003_auto_20210510_1148.py +++ b/src/cms/migrations/0003_auto_20210510_1148.py @@ -6,75 +6,130 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0002_migrate_issue_types'), + ("cms", "0002_migrate_issue_types"), ] operations = [ migrations.AddField( - model_name='navigationitem', - name='link_name_cy', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_cy", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='navigationitem', - name='link_name_de', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_de", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='navigationitem', - name='link_name_en', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_en", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='navigationitem', - name='link_name_fr', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_fr", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='page', - name='content_cy', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_cy", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='content_de', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_de", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='content_en', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_en", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='content_fr', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_fr", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_cy', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_cy", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_de', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_de", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_en', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_en", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_fr', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_fr", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AlterField( - model_name='page', - name='name', - field=models.CharField(help_text='The relative URL path to the page, using lowercase letters and hyphens. For example, a page about research integrity might be “research-integrity”.', max_length=300, verbose_name='Link'), + model_name="page", + name="name", + field=models.CharField( + help_text="The relative URL path to the page, using lowercase letters and hyphens. For example, a page about research integrity might be “research-integrity”.", + max_length=300, + verbose_name="Link", + ), ), ] diff --git a/src/cms/migrations/0004_submissionitem.py b/src/cms/migrations/0004_submissionitem.py index 00b471697d..4946f9eb85 100644 --- a/src/cms/migrations/0004_submissionitem.py +++ b/src/cms/migrations/0004_submissionitem.py @@ -7,35 +7,58 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0051_auto_20210513_1206'), - ('journal', '0043_auto_20210518_1616'), - ('cms', '0003_auto_20210510_1148'), + ("core", "0051_auto_20210513_1206"), + ("journal", "0043_auto_20210518_1616"), + ("cms", "0003_auto_20210510_1148"), ] operations = [ migrations.CreateModel( - name='SubmissionItem', + name="SubmissionItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=255)), - ('title_en', models.CharField(max_length=255, null=True)), - ('title_fr', models.CharField(max_length=255, null=True)), - ('title_de', models.CharField(max_length=255, null=True)), - ('title_cy', models.CharField(max_length=255, null=True)), - ('text', models.TextField()), - ('text_en', models.TextField(null=True)), - ('text_fr', models.TextField(null=True)), - ('text_de', models.TextField(null=True)), - ('text_cy', models.TextField(null=True)), - ('order', models.IntegerField(default=99)), - ('existing_setting', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Setting')), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=255)), + ("title_en", models.CharField(max_length=255, null=True)), + ("title_fr", models.CharField(max_length=255, null=True)), + ("title_de", models.CharField(max_length=255, null=True)), + ("title_cy", models.CharField(max_length=255, null=True)), + ("text", models.TextField()), + ("text_en", models.TextField(null=True)), + ("text_fr", models.TextField(null=True)), + ("text_de", models.TextField(null=True)), + ("text_cy", models.TextField(null=True)), + ("order", models.IntegerField(default=99)), + ( + "existing_setting", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Setting", + ), + ), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), migrations.AlterUniqueTogether( - name='submissionitem', - unique_together=set([('journal', 'title'), ('journal', 'existing_setting')]), + name="submissionitem", + unique_together=set( + [("journal", "title"), ("journal", "existing_setting")] + ), ), ] diff --git a/src/cms/migrations/0005_auto_20210518_1619.py b/src/cms/migrations/0005_auto_20210518_1619.py index 3728a253df..fdb6881bca 100644 --- a/src/cms/migrations/0005_auto_20210518_1619.py +++ b/src/cms/migrations/0005_auto_20210518_1619.py @@ -10,31 +10,35 @@ settings_to_migrate = [ - {'group': 'general', 'name': 'journal_description', 'title': 'About'}, - {'group': 'general', 'name': 'focus_and_scope', 'title': 'Focus and Scope'}, - {'group': 'general', 'name': 'submission_checklist', 'title': 'Submission Checklist'}, - {'group': 'general', 'name': 'copyright_notice', 'title': 'Copyright Notice'}, - {'group': 'general', 'name': 'peer_review_info', 'title': 'Peer Review'}, - {'group': 'special', 'name': 'licences', 'title': 'licences'}, - {'group': 'general', 'name': 'publication_fees', 'title': 'Publication Fees'}, - {'group': 'general', 'name': 'publication_cycle', 'title': 'Publication Cycle'}, - {'group': 'special', 'name': 'sections', 'title': 'sections'}, + {"group": "general", "name": "journal_description", "title": "About"}, + {"group": "general", "name": "focus_and_scope", "title": "Focus and Scope"}, + { + "group": "general", + "name": "submission_checklist", + "title": "Submission Checklist", + }, + {"group": "general", "name": "copyright_notice", "title": "Copyright Notice"}, + {"group": "general", "name": "peer_review_info", "title": "Peer Review"}, + {"group": "special", "name": "licences", "title": "licences"}, + {"group": "general", "name": "publication_fees", "title": "Publication Fees"}, + {"group": "general", "name": "publication_cycle", "title": "Publication Cycle"}, + {"group": "special", "name": "sections", "title": "sections"}, ] def setup_submission_items(apps, schema_editor): - SubmissionItem = apps.get_model('cms', 'SubmissionItem') - Journal = apps.get_model('journal', 'Journal') - Setting = apps.get_model('core', 'Setting') + SubmissionItem = apps.get_model("cms", "SubmissionItem") + Journal = apps.get_model("journal", "Journal") + Setting = apps.get_model("core", "Setting") journals = Journal.objects.all() for journal in journals: for i, setting in enumerate(settings_to_migrate): - if not setting.get('group') == 'special': + if not setting.get("group") == "special": setting_obj = Setting.objects.get( - group__name=setting.get('group'), - name=setting.get('name'), + group__name=setting.get("group"), + name=setting.get("name"), ) else: setting_obj = None @@ -44,14 +48,13 @@ def setup_submission_items(apps, schema_editor): journal=journal, order=i, existing_setting=setting_obj, - title=setting.get('title'), + title=setting.get("title"), ) class Migration(migrations.Migration): - dependencies = [ - ('cms', '0004_submissionitem'), + ("cms", "0004_submissionitem"), ] operations = [ diff --git a/src/cms/migrations/0006_auto_20210618_1305.py b/src/cms/migrations/0006_auto_20210618_1305.py index dc60e73482..8d5e862676 100644 --- a/src/cms/migrations/0006_auto_20210618_1305.py +++ b/src/cms/migrations/0006_auto_20210618_1305.py @@ -6,39 +6,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0005_auto_20210518_1619'), + ("cms", "0005_auto_20210518_1619"), ] operations = [ migrations.AlterModelOptions( - name='submissionitem', - options={'ordering': ('order', 'title')}, + name="submissionitem", + options={"ordering": ("order", "title")}, ), migrations.AlterField( - model_name='submissionitem', - name='text', + model_name="submissionitem", + name="text", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_cy', + model_name="submissionitem", + name="text_cy", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_de', + model_name="submissionitem", + name="text_de", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_en', + model_name="submissionitem", + name="text_en", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_fr', + model_name="submissionitem", + name="text_fr", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/cms/migrations/0007_auto_20210629_0641.py b/src/cms/migrations/0007_auto_20210629_0641.py index 0efca0d963..490a151a6d 100644 --- a/src/cms/migrations/0007_auto_20210629_0641.py +++ b/src/cms/migrations/0007_auto_20210629_0641.py @@ -6,35 +6,47 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0006_auto_20210618_1305'), + ("cms", "0006_auto_20210618_1305"), ] operations = [ migrations.AddField( - model_name='navigationitem', - name='link_name_nl', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_nl", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='page', - name='content_nl', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_nl", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_nl', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_nl", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AddField( - model_name='submissionitem', - name='text_nl', + model_name="submissionitem", + name="text_nl", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='submissionitem', - name='title_nl', + model_name="submissionitem", + name="title_nl", field=models.CharField(max_length=255, null=True), ), ] diff --git a/src/cms/migrations/0008_auto_20210825_1432.py b/src/cms/migrations/0008_auto_20210825_1432.py index f5f5115192..cf34dcf829 100644 --- a/src/cms/migrations/0008_auto_20210825_1432.py +++ b/src/cms/migrations/0008_auto_20210825_1432.py @@ -6,19 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0007_auto_20210629_0641'), + ("cms", "0007_auto_20210629_0641"), ] operations = [ migrations.AlterModelOptions( - name='navigationitem', - options={'ordering': ('sequence',)}, + name="navigationitem", + options={"ordering": ("sequence",)}, ), migrations.AlterField( - model_name='navigationitem', - name='link', - field=models.CharField(blank=True, help_text='In most cases, this should be the the relative URL path to the page. The relative path is formed from 1) the journal code (acronym or abbreviation) if your journal homepage URL ends with the journal code, 2) the word “site”, and 3) whatever you put into the Link field for the corresponding page. For example, to link to a custom page you created, if the journal homepage URL is “example.com/abc”, and you put “research-integrity” in the Link field for the page, then the Link field for the nav item should be “abc/site/research-integrity”. For top-level nav items that should not also appear as sub-nav items (under themselves), leave the Link field empty. For external links, it should be an absolute URL.', max_length=100, null=True), + model_name="navigationitem", + name="link", + field=models.CharField( + blank=True, + help_text="In most cases, this should be the the relative URL path to the page. The relative path is formed from 1) the journal code (acronym or abbreviation) if your journal homepage URL ends with the journal code, 2) the word “site”, and 3) whatever you put into the Link field for the corresponding page. For example, to link to a custom page you created, if the journal homepage URL is “example.com/abc”, and you put “research-integrity” in the Link field for the page, then the Link field for the nav item should be “abc/site/research-integrity”. For top-level nav items that should not also appear as sub-nav items (under themselves), leave the Link field empty. For external links, it should be an absolute URL.", + max_length=100, + null=True, + ), ), ] diff --git a/src/cms/migrations/0009_mediafile.py b/src/cms/migrations/0009_mediafile.py index fa390b3830..2ee9959ca0 100644 --- a/src/cms/migrations/0009_mediafile.py +++ b/src/cms/migrations/0009_mediafile.py @@ -10,21 +10,42 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0046_auto_20210922_1436'), - ('cms', '0008_auto_20210825_1432'), + ("journal", "0046_auto_20210922_1436"), + ("cms", "0008_auto_20210825_1432"), ] operations = [ migrations.CreateModel( - name='MediaFile', + name="MediaFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('label', models.CharField(max_length=255)), - ('file', models.FileField(storage=core.file_system.JanewayFileSystemStorage(), upload_to=cms.models.upload_to_media_files)), - ('uploaded', models.DateTimeField(default=django.utils.timezone.now)), - ('journal', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("label", models.CharField(max_length=255)), + ( + "file", + models.FileField( + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=cms.models.upload_to_media_files, + ), + ), + ("uploaded", models.DateTimeField(default=django.utils.timezone.now)), + ( + "journal", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), ] diff --git a/src/cms/migrations/0010_auto_20230208_1233.py b/src/cms/migrations/0010_auto_20230208_1233.py index 5408a5c183..a4a57d6e51 100644 --- a/src/cms/migrations/0010_auto_20230208_1233.py +++ b/src/cms/migrations/0010_auto_20230208_1233.py @@ -6,35 +6,47 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0009_mediafile'), + ("cms", "0009_mediafile"), ] operations = [ migrations.AddField( - model_name='navigationitem', - name='link_name_en_us', - field=models.CharField(help_text='The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)', max_length=100, null=True, verbose_name='Display name'), + model_name="navigationitem", + name="link_name_en_us", + field=models.CharField( + help_text="The text that will appear in the nav bar (e.g. “About” or “Research Integrity”)", + max_length=100, + null=True, + verbose_name="Display name", + ), ), migrations.AddField( - model_name='page', - name='content_en_us', - field=models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), + model_name="page", + name="content_en_us", + field=models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), ), migrations.AddField( - model_name='page', - name='display_name_en_us', - field=models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True), + model_name="page", + name="display_name_en_us", + field=models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), ), migrations.AddField( - model_name='submissionitem', - name='text_en_us', + model_name="submissionitem", + name="text_en_us", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='submissionitem', - name='title_en_us', + model_name="submissionitem", + name="title_en_us", field=models.CharField(max_length=255, null=True), ), ] diff --git a/src/cms/migrations/0011_auto_20230317_1534.py b/src/cms/migrations/0011_auto_20230317_1534.py index 0397b6c435..4ebaa71311 100644 --- a/src/cms/migrations/0011_auto_20230317_1534.py +++ b/src/cms/migrations/0011_auto_20230317_1534.py @@ -5,21 +5,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), - ('cms', '0010_auto_20230208_1233'), + ("core", "0081_alter_account_preferred_timezone"), + ("cms", "0010_auto_20230208_1233"), ] operations = [ migrations.AlterField( - model_name='navigationitem', - name='page', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.page'), + model_name="navigationitem", + name="page", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="cms.page", + ), ), migrations.AlterField( - model_name='submissionitem', - name='existing_setting', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.setting'), + model_name="submissionitem", + name="existing_setting", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.setting", + ), ), ] diff --git a/src/cms/migrations/0012_navigationitem_for_footer.py b/src/cms/migrations/0012_navigationitem_for_footer.py index d3169affb7..971d5ffe34 100644 --- a/src/cms/migrations/0012_navigationitem_for_footer.py +++ b/src/cms/migrations/0012_navigationitem_for_footer.py @@ -4,19 +4,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0011_auto_20230317_1534'), + ("cms", "0011_auto_20230317_1534"), ] operations = [ migrations.AddField( - model_name='navigationitem', - name='for_footer', + model_name="navigationitem", + name="for_footer", field=models.BooleanField( default=False, - help_text='Whether this item should appear in the footer. ' - 'Not implemented for all themes.', + help_text="Whether this item should appear in the footer. " + "Not implemented for all themes.", ), ), ] diff --git a/src/cms/migrations/0013_navigationitem_extend_to_journals.py b/src/cms/migrations/0013_navigationitem_extend_to_journals.py index f2ecda118a..e6d681e3fd 100644 --- a/src/cms/migrations/0013_navigationitem_extend_to_journals.py +++ b/src/cms/migrations/0013_navigationitem_extend_to_journals.py @@ -4,20 +4,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0012_navigationitem_for_footer'), + ("cms", "0012_navigationitem_for_footer"), ] operations = [ migrations.AddField( - model_name='navigationitem', - name='extend_to_journals', + model_name="navigationitem", + name="extend_to_journals", field=models.BooleanField( default=False, - help_text='Whether this item should be ' - 'extended to journal websites. ' - 'Only implemented for footer links.', + help_text="Whether this item should be " + "extended to journal websites. " + "Only implemented for footer links.", ), ), ] diff --git a/src/cms/migrations/0013_page_template.py b/src/cms/migrations/0013_page_template.py index 5de71095e5..fe047728ad 100644 --- a/src/cms/migrations/0013_page_template.py +++ b/src/cms/migrations/0013_page_template.py @@ -4,15 +4,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0012_navigationitem_for_footer'), + ("cms", "0012_navigationitem_for_footer"), ] operations = [ migrations.AddField( - model_name='page', - name='template', - field=models.CharField(blank=True, help_text='The custom template to use instead of the content field.', max_length=100), + model_name="page", + name="template", + field=models.CharField( + blank=True, + help_text="The custom template to use instead of the content field.", + max_length=100, + ), ), ] diff --git a/src/cms/migrations/0014_page_support_copy_paste.py b/src/cms/migrations/0014_page_support_copy_paste.py index a5001da311..6e1ba3a28b 100644 --- a/src/cms/migrations/0014_page_support_copy_paste.py +++ b/src/cms/migrations/0014_page_support_copy_paste.py @@ -4,9 +4,8 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0013_navigationitem_extend_to_journals'), + ("cms", "0013_navigationitem_extend_to_journals"), ] # Why two operations with support_copy_paste? @@ -18,25 +17,25 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( - model_name='page', - name='support_copy_paste', + model_name="page", + name="support_copy_paste", field=models.BooleanField( default=False, ), ), migrations.AlterField( - model_name='page', - name='support_copy_paste', + model_name="page", + name="support_copy_paste", field=models.BooleanField( default=True, - help_text='Turn this on if copy-pasting content ' - 'from a word processor, ' - 'or using the toolbar to format text. ' - 'It tells Janeway to clear out formatting ' - 'that does not play nice. ' - 'Turn it off and leave it off if anyone has ' - 'added custom HTML or CSS using the code view, ' - 'since it might remove custom code.', + help_text="Turn this on if copy-pasting content " + "from a word processor, " + "or using the toolbar to format text. " + "It tells Janeway to clear out formatting " + "that does not play nice. " + "Turn it off and leave it off if anyone has " + "added custom HTML or CSS using the code view, " + "since it might remove custom code.", ), ), ] diff --git a/src/cms/migrations/0015_historicalpage.py b/src/cms/migrations/0015_historicalpage.py index 3df914d738..4c61880f68 100644 --- a/src/cms/migrations/0015_historicalpage.py +++ b/src/cms/migrations/0015_historicalpage.py @@ -7,49 +7,187 @@ class Migration(migrations.Migration): - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), + ("contenttypes", "0002_remove_content_type_name"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('cms', '0014_page_support_copy_paste'), + ("cms", "0014_page_support_copy_paste"), ] operations = [ migrations.CreateModel( - name='HistoricalPage', + name="HistoricalPage", fields=[ - ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), - ('support_copy_paste', models.BooleanField(default=True, help_text='Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('name', models.CharField(help_text='The relative URL path to the page, using lowercase letters and hyphens. For example, a page about research integrity might be “research-integrity”.', max_length=300, verbose_name='Link')), - ('display_name', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100)), - ('display_name_en', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('display_name_en_us', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('display_name_fr', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('display_name_de', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('display_name_nl', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('display_name_cy', models.CharField(help_text='Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).', max_length=100, null=True)), - ('content', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_en', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_en_us', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_fr', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_de', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_nl', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('content_cy', models.TextField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True)), - ('is_markdown', models.BooleanField(default=True)), - ('edited', models.DateTimeField(blank=True, editable=False)), - ('history_id', models.AutoField(primary_key=True, serialize=False)), - ('history_date', models.DateTimeField(db_index=True)), - ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('content_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='contenttypes.contenttype')), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.IntegerField( + auto_created=True, blank=True, db_index=True, verbose_name="ID" + ), + ), + ( + "support_copy_paste", + models.BooleanField( + default=True, + help_text="Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "name", + models.CharField( + help_text="The relative URL path to the page, using lowercase letters and hyphens. For example, a page about research integrity might be “research-integrity”.", + max_length=300, + verbose_name="Link", + ), + ), + ( + "display_name", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + ), + ), + ( + "display_name_en", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "display_name_en_us", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "display_name_fr", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "display_name_de", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "display_name_nl", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "display_name_cy", + models.CharField( + help_text="Name of the page, in 100 characters or fewer, displayed in the nav and in the top-level heading on the page (e.g. “Research Integrity”).", + max_length=100, + null=True, + ), + ), + ( + "content", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_en", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_en_us", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_fr", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_de", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_nl", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ( + "content_cy", + models.TextField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + ("is_markdown", models.BooleanField(default=True)), + ("edited", models.DateTimeField(blank=True, editable=False)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ( + "history_type", + models.CharField( + choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], + max_length=1, + ), + ), + ( + "content_type", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "history_user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'historical page', - 'verbose_name_plural': 'historical pages', - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': ('history_date', 'history_id'), + "verbose_name": "historical page", + "verbose_name_plural": "historical pages", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), }, bases=(simple_history.models.HistoricalChanges, models.Model), ), diff --git a/src/cms/migrations/0016_auto_20240312_0922.py b/src/cms/migrations/0016_auto_20240312_0922.py index 7be9eb2cd5..dfee185465 100644 --- a/src/cms/migrations/0016_auto_20240312_0922.py +++ b/src/cms/migrations/0016_auto_20240312_0922.py @@ -5,123 +5,178 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0015_historicalpage'), + ("cms", "0015_historicalpage"), ] operations = [ migrations.RemoveField( - model_name='historicalpage', - name='support_copy_paste', + model_name="historicalpage", + name="support_copy_paste", ), migrations.RemoveField( - model_name='page', - name='support_copy_paste', - ), - migrations.AlterField( - model_name='historicalpage', - name='content', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_cy', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_de', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_en', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_en_us', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_fr', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='historicalpage', - name='content_nl', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_cy', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_de', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_en', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_en_us', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_fr', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='page', - name='content_nl', - field=core.model_utils.JanewayBleachField(blank=True, help_text='The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).', null=True), - ), - migrations.AlterField( - model_name='submissionitem', - name='text', + model_name="page", + name="support_copy_paste", + ), + migrations.AlterField( + model_name="historicalpage", + name="content", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_cy", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_de", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_en", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_en_us", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_fr", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="historicalpage", + name="content_nl", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_cy", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_de", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_en", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_en_us", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_fr", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="page", + name="content_nl", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The content of the page. For headings, we recommend using the Style dropdown (looks like a wand) and selecting a heading level from 2 to 6, as the display name field occupies the place of heading level 1. Note that copying and pasting from a word processor can produce unwanted results, but you can use Remove Font Style (looks like an eraser) to remove some unwanted formatting. To edit the page as HTML, turn on the Code View (<>).", + null=True, + ), + ), + migrations.AlterField( + model_name="submissionitem", + name="text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_cy', + model_name="submissionitem", + name="text_cy", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_de', + model_name="submissionitem", + name="text_de", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_en', + model_name="submissionitem", + name="text_en", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_en_us', + model_name="submissionitem", + name="text_en_us", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_fr', + model_name="submissionitem", + name="text_fr", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='submissionitem', - name='text_nl', + model_name="submissionitem", + name="text_nl", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), ] diff --git a/src/cms/migrations/0016_merge_0013_page_template_0015_historicalpage.py b/src/cms/migrations/0016_merge_0013_page_template_0015_historicalpage.py index f5b125288c..55e43d7cfb 100644 --- a/src/cms/migrations/0016_merge_0013_page_template_0015_historicalpage.py +++ b/src/cms/migrations/0016_merge_0013_page_template_0015_historicalpage.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0013_page_template'), - ('cms', '0015_historicalpage'), + ("cms", "0013_page_template"), + ("cms", "0015_historicalpage"), ] - operations = [ - ] + operations = [] diff --git a/src/cms/migrations/0017_historicalpage_template.py b/src/cms/migrations/0017_historicalpage_template.py index f9599b69d8..f7e473ac93 100644 --- a/src/cms/migrations/0017_historicalpage_template.py +++ b/src/cms/migrations/0017_historicalpage_template.py @@ -4,19 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0016_merge_0013_page_template_0015_historicalpage'), + ("cms", "0016_merge_0013_page_template_0015_historicalpage"), ] operations = [ migrations.AddField( - model_name='historicalpage', - name='template', + model_name="historicalpage", + name="template", field=models.CharField( blank=True, - help_text='The custom template to use instead of the content ' - 'field.', + help_text="The custom template to use instead of the content " "field.", max_length=100, ), ), diff --git a/src/cms/migrations/0018_merge_20240315_1649.py b/src/cms/migrations/0018_merge_20240315_1649.py index ac77682c25..489d18bae7 100644 --- a/src/cms/migrations/0018_merge_20240315_1649.py +++ b/src/cms/migrations/0018_merge_20240315_1649.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0016_auto_20240312_0922'), - ('cms', '0017_historicalpage_template'), + ("cms", "0016_auto_20240312_0922"), + ("cms", "0017_historicalpage_template"), ] - operations = [ - ] + operations = [] diff --git a/src/cms/migrations/0019_auto_20240328_1743.py b/src/cms/migrations/0019_auto_20240328_1743.py index 84bbd1e200..06baf3016a 100644 --- a/src/cms/migrations/0019_auto_20240328_1743.py +++ b/src/cms/migrations/0019_auto_20240328_1743.py @@ -4,20 +4,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('cms', '0018_merge_20240315_1649'), + ("cms", "0018_merge_20240315_1649"), ] operations = [ migrations.AddField( - model_name='historicalpage', - name='display_toc', - field=models.BooleanField(default=False, help_text='When enabled this page will display a thinner reading pane with a table of contents side bar.', verbose_name='Display table of contents'), + model_name="historicalpage", + name="display_toc", + field=models.BooleanField( + default=False, + help_text="When enabled this page will display a thinner reading pane with a table of contents side bar.", + verbose_name="Display table of contents", + ), ), migrations.AddField( - model_name='page', - name='display_toc', - field=models.BooleanField(default=False, help_text='When enabled this page will display a thinner reading pane with a table of contents side bar.', verbose_name='Display table of contents'), + model_name="page", + name="display_toc", + field=models.BooleanField( + default=False, + help_text="When enabled this page will display a thinner reading pane with a table of contents side bar.", + verbose_name="Display table of contents", + ), ), ] diff --git a/src/cms/models.py b/src/cms/models.py index 0bf820408e..0c2a102469 100755 --- a/src/cms/models.py +++ b/src/cms/models.py @@ -18,100 +18,104 @@ class Page(models.Model): - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='page_content', null=True) + content_type = models.ForeignKey( + ContentType, on_delete=models.CASCADE, related_name="page_content", null=True + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") name = models.CharField( max_length=300, - help_text='The relative URL path to the page, using lowercase ' - 'letters and hyphens. For example, a page about ' - 'research integrity might be “research-integrity”.', - verbose_name='Link', + help_text="The relative URL path to the page, using lowercase " + "letters and hyphens. For example, a page about " + "research integrity might be “research-integrity”.", + verbose_name="Link", ) display_name = models.CharField( max_length=100, - help_text='Name of the page, in 100 characters or fewer, ' - 'displayed in the nav and in the top-level heading ' - 'on the page (e.g. “Research Integrity”).', + help_text="Name of the page, in 100 characters or fewer, " + "displayed in the nav and in the top-level heading " + "on the page (e.g. “Research Integrity”).", ) template = models.CharField( blank=True, max_length=100, - help_text='The custom template to use instead of the content field.', + help_text="The custom template to use instead of the content field.", ) content = JanewayBleachField( null=True, blank=True, - help_text='The content of the page. For headings, we recommend ' - 'using the Style dropdown (looks like a wand) and ' - 'selecting a heading level from 2 to 6, as the display ' - 'name field occupies the place of heading level 1. ' - 'Note that copying and pasting from a word processor ' - 'can produce unwanted results, but you can use Remove ' - 'Font Style (looks like an eraser) to remove some ' - 'unwanted formatting. To edit the page as HTML, ' - 'turn on the Code View (<>).', + help_text="The content of the page. For headings, we recommend " + "using the Style dropdown (looks like a wand) and " + "selecting a heading level from 2 to 6, as the display " + "name field occupies the place of heading level 1. " + "Note that copying and pasting from a word processor " + "can produce unwanted results, but you can use Remove " + "Font Style (looks like an eraser) to remove some " + "unwanted formatting. To edit the page as HTML, " + "turn on the Code View (<>).", ) is_markdown = models.BooleanField(default=True) edited = models.DateTimeField(auto_now=timezone.now) display_toc = models.BooleanField( default=False, - help_text='When enabled this page will display a thinner reading pane ' - 'with a table of contents side bar.', - verbose_name='Display table of contents', + help_text="When enabled this page will display a thinner reading pane " + "with a table of contents side bar.", + verbose_name="Display table of contents", ) # history = HistoricalRecords() is defined in cms.translation # for compatibility with django-modeltranslation def __str__(self): - return u'{0} - {1}'.format(self.content_type, self.display_name) + return "{0} - {1}".format(self.content_type, self.display_name) class NavigationItem(models.Model): - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='nav_content', null=True) + content_type = models.ForeignKey( + ContentType, on_delete=models.CASCADE, related_name="nav_content", null=True + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") link_name = models.CharField( max_length=100, - help_text='The text that will appear in the nav bar ' - '(e.g. “About” or “Research Integrity”)', - verbose_name='Display name', + help_text="The text that will appear in the nav bar " + "(e.g. “About” or “Research Integrity”)", + verbose_name="Display name", ) link = models.CharField( max_length=100, blank=True, null=True, - help_text='In most cases, this should be the the ' - 'relative URL path to the page. The ' - 'relative path is formed from 1) the ' - 'journal code (acronym or abbreviation) ' - 'if your journal homepage URL ends with ' - 'the journal code, 2) the word “site”, and ' - '3) whatever you put into the Link field for ' - 'the corresponding page. For example, ' - 'to link to a custom page you created, ' - 'if the journal homepage URL is “example.com/abc”, ' - 'and you put “research-integrity” in the Link field ' - 'for the page, then the Link field for the nav item ' - 'should be “abc/site/research-integrity”. For top-level ' - 'nav items that should not also appear as sub-nav ' - 'items (under themselves), leave the Link field empty. ' - 'For external links, it should be an absolute URL.', + help_text="In most cases, this should be the the " + "relative URL path to the page. The " + "relative path is formed from 1) the " + "journal code (acronym or abbreviation) " + "if your journal homepage URL ends with " + "the journal code, 2) the word “site”, and " + "3) whatever you put into the Link field for " + "the corresponding page. For example, " + "to link to a custom page you created, " + "if the journal homepage URL is “example.com/abc”, " + "and you put “research-integrity” in the Link field " + "for the page, then the Link field for the nav item " + "should be “abc/site/research-integrity”. For top-level " + "nav items that should not also appear as sub-nav " + "items (under themselves), leave the Link field empty. " + "For external links, it should be an absolute URL.", ) is_external = models.BooleanField( default=False, - help_text='Whether the link is to an external website.', + help_text="Whether the link is to an external website.", ) sequence = models.IntegerField( default=99, - help_text='The order in which custom nav items appear relative ' - 'to other custom nav items. Note that fixed (default) ' - 'nav items do not have a sequence field, so you have ' - 'to replace them with custom elements to change their ' - 'order.', + help_text="The order in which custom nav items appear relative " + "to other custom nav items. Note that fixed (default) " + "nav items do not have a sequence field, so you have " + "to replace them with custom elements to change their " + "order.", ) page = models.ForeignKey( Page, @@ -122,31 +126,31 @@ class NavigationItem(models.Model): has_sub_nav = models.BooleanField( default=False, verbose_name="Has sub navigation", - help_text='Whether this item has sub-nav items under it.', + help_text="Whether this item has sub-nav items under it.", ) top_level_nav = models.ForeignKey( "self", blank=True, null=True, verbose_name="Top-level nav item", - help_text='If this is a sub-nav item, which top-level ' - 'item should it go under?', + help_text="If this is a sub-nav item, which top-level " + "item should it go under?", on_delete=models.CASCADE, ) for_footer = models.BooleanField( default=False, - help_text='Whether this item should appear in the footer. ' - 'Not implemented for all themes.', + help_text="Whether this item should appear in the footer. " + "Not implemented for all themes.", ) extend_to_journals = models.BooleanField( default=False, - help_text='Whether this item should be ' - 'extended to journal websites. ' - 'Only implemented for footer links.', + help_text="Whether this item should be " + "extended to journal websites. " + "Only implemented for footer links.", ) class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): return self.link_name @@ -166,7 +170,7 @@ def url(self): # alias for backwards compatibility with templates if self.link: return self.build_url_for_request - return '' + return "" @classmethod def toggle_collection_nav(cls, issue_type): @@ -192,10 +196,9 @@ def toggle_collection_nav(cls, issue_type): @classmethod def get_issue_types_for_nav(cls, journal): for issue_type in journal.issuetype_set.filter( - ~Q(code="issue") # Issues have their own navigation + ~Q(code="issue") # Issues have their own navigation ): - content_type = ContentType.objects.get_for_model( - issue_type.journal) + content_type = ContentType.objects.get_for_model(issue_type.journal) if not cls.objects.filter( content_type=content_type, object_id=issue_type.journal.pk, @@ -209,23 +212,24 @@ class SubmissionItem(models.Model): Model containing information to render the Submission page. SubmissionItems is registered for translation in cms.translation. """ + journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) title = models.CharField(max_length=255) text = JanewayBleachField(blank=True, null=True) order = models.IntegerField(default=99) existing_setting = models.ForeignKey( - 'core.Setting', + "core.Setting", blank=True, null=True, on_delete=models.SET_NULL, ) class Meta: - ordering = ('order', 'title') - unique_together = (('journal', 'existing_setting'), ('journal', 'title')) + ordering = ("order", "title") + unique_together = (("journal", "existing_setting"), ("journal", "title")) def get_display_text(self): if self.existing_setting: @@ -254,10 +258,10 @@ def upload_to_media_files(instance, filename): class MediaFile(models.Model): label = models.CharField(max_length=255) file = models.FileField( - upload_to=upload_to_media_files, - storage=JanewayFileSystemStorage()) + upload_to=upload_to_media_files, storage=JanewayFileSystemStorage() + ) journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", null=True, blank=True, on_delete=models.CASCADE, diff --git a/src/cms/tests/test_models.py b/src/cms/tests/test_models.py index 4a66e98a49..1122f4ce6a 100644 --- a/src/cms/tests/test_models.py +++ b/src/cms/tests/test_models.py @@ -30,7 +30,7 @@ def setUpClass(cls): cls.press_cms_page = helpers.create_cms_page(press_type, cls.press.pk) journal_type = ContentType.objects.get_for_model(cls.journal_one) cls.journal_cms_page = helpers.create_cms_page(journal_type, cls.journal_one.pk) - setting_handler.save_setting('general', 'custom_cms_templates', None, 'custom') + setting_handler.save_setting("general", "custom_cms_templates", None, "custom") @classmethod def tearDownClass(cls): diff --git a/src/cms/tests/test_site_search.py b/src/cms/tests/test_site_search.py index a1d1654fc7..b7371020cc 100644 --- a/src/cms/tests/test_site_search.py +++ b/src/cms/tests/test_site_search.py @@ -14,7 +14,6 @@ class TestSiteSearch(TestCase): - @classmethod def setUpClass(cls): cls.press = press_models.Press.objects.first() @@ -22,10 +21,9 @@ def setUpClass(cls): cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() cls.docs_label = os.path.join( - settings.SITE_SEARCH_DIR, - f'_press_{ cls.press.pk }_documents.json' + settings.SITE_SEARCH_DIR, f"_press_{ cls.press.pk }_documents.json" ) - cls.searchable_page = ''' + cls.searchable_page = """ Seasons @@ -57,7 +55,7 @@ def setUpClass(cls): - ''' + """ @classmethod def tearDownClass(cls): @@ -71,175 +69,165 @@ def setUp(self): def tearDown(self): del self.docs try: - call_command('delete_site_search_data', '--press_id', self.press.pk) + call_command("delete_site_search_data", "--press_id", self.press.pk) except FileNotFoundError: pass def test_add_search_index_document(self): docs = cms_logic.add_search_index_document( self.docs, - 'example.org', - 'Example', - 'Hello', + "example.org", + "Example", + "Hello", ) self.assertDictEqual( docs[-1], { - 'id': 0, - 'url': 'example.org', - 'name': 'Example', - 'text': 'Hello', - } + "id": 0, + "url": "example.org", + "name": "Example", + "text": "Hello", + }, ) self.assertEqual(len(docs), 1) docs = cms_logic.add_search_index_document( docs, - 'example.org/2', - 'Other', - 'Hi', + "example.org/2", + "Other", + "Hi", ) - self.assertEqual(docs[-1]['id'], 1) + self.assertEqual(docs[-1]["id"], 1) self.assertEqual(len(docs), 2) def test_gobble_sibling_text(self): - soup = BeautifulSoup(self.searchable_page, 'html.parser') - spring = soup.find(id='spring') + soup = BeautifulSoup(self.searchable_page, "html.parser") + spring = soup.find(id="spring") spring_text = cms_logic.gobble_sibling_text(spring.next_sibling, spring) - self.assertIn('March', spring_text) - self.assertNotIn('June', spring_text) - summer = soup.find(id='summer') - summer_text = cms_logic.gobble_sibling_text( - summer.parent.next_sibling, - summer - ) - self.assertIn('June', summer_text) - self.assertNotIn('August', summer_text) + self.assertIn("March", spring_text) + self.assertNotIn("June", spring_text) + summer = soup.find(id="summer") + summer_text = cms_logic.gobble_sibling_text(summer.parent.next_sibling, summer) + self.assertIn("June", summer_text) + self.assertNotIn("August", summer_text) def test_get_text_for_parent(self): - soup = BeautifulSoup(self.searchable_page, 'html.parser') - summer = soup.find(id='summer') + soup = BeautifulSoup(self.searchable_page, "html.parser") + summer = soup.find(id="summer") summer_text = cms_logic.get_text_for_parent(summer.parent, summer) - self.assertIn('June', summer_text) - self.assertNotIn('September', summer_text) + self.assertIn("June", summer_text) + self.assertNotIn("September", summer_text) def test_get_text_for_header(self): - soup = BeautifulSoup(self.searchable_page, 'html.parser') - spring = soup.find(id='spring') + soup = BeautifulSoup(self.searchable_page, "html.parser") + spring = soup.find(id="spring") spring_text = cms_logic.get_text_for_header(spring) - self.assertIn('March', spring_text) - self.assertIn('May Day', spring_text) - summer = soup.find(id='summer') + self.assertIn("March", spring_text) + self.assertIn("May Day", spring_text) + summer = soup.find(id="summer") summer_text = cms_logic.get_text_for_header(summer) - self.assertIn('June', summer_text) + self.assertIn("June", summer_text) - @patch('cms.logic.add_search_index_document') + @patch("cms.logic.add_search_index_document") def test_add_part_as_doc(self, add_doc): - cms_logic.add_part_as_doc(self.docs, 'example.org', '', 'May') + cms_logic.add_part_as_doc(self.docs, "example.org", "", "May") add_doc.assert_not_called() - cms_logic.add_part_as_doc(self.docs, 'example.org', 'Spring', '') + cms_logic.add_part_as_doc(self.docs, "example.org", "Spring", "") add_doc.assert_not_called() - cms_logic.add_part_as_doc(self.docs, 'example.org', 'Spring', 'May') - add_doc.assert_called_with(self.docs, 'example.org', 'Spring', 'May') + cms_logic.add_part_as_doc(self.docs, "example.org", "Spring", "May") + add_doc.assert_called_with(self.docs, "example.org", "Spring", "May") - @patch('cms.logic.add_part_as_doc') + @patch("cms.logic.add_part_as_doc") def test_add_searchable_page_parts(self, add_part): - soup = BeautifulSoup(self.searchable_page, 'html.parser') - body = soup.find('body') - self.assertTrue(soup.find(id='winter')) - docs = cms_logic.add_searchable_page_parts(self.docs, 'example.org', body) + soup = BeautifulSoup(self.searchable_page, "html.parser") + body = soup.find("body") + self.assertTrue(soup.find(id="winter")) + docs = cms_logic.add_searchable_page_parts(self.docs, "example.org", body) add_part.assert_called_with( docs, - 'example.org#winter', - 'Winter', - 'New Year', + "example.org#winter", + "Winter", + "New Year", ) - self.assertFalse(soup.find(id='winter')) + self.assertFalse(soup.find(id="winter")) def test_get_name(self): - html_with_h1 = BeautifulSoup('

h1

', 'html.parser') + html_with_h1 = BeautifulSoup("

h1

", "html.parser") name = cms_logic.get_name(html_with_h1) - self.assertEqual(name, 'h1') - html_with_title = BeautifulSoup('title', 'html.parser') + self.assertEqual(name, "h1") + html_with_title = BeautifulSoup("title", "html.parser") name = cms_logic.get_name(html_with_title) - self.assertEqual(name, 'title') + self.assertEqual(name, "title") def test_get_body(self): - soup = BeautifulSoup(self.searchable_page, 'html.parser') + soup = BeautifulSoup(self.searchable_page, "html.parser") body = cms_logic.get_body(soup) - self.assertNotIn('Do not index me', body.get_text()) + self.assertNotIn("Do not index me", body.get_text()) def test_excluded_urls(self): self.assertListEqual( [self.journal_one.site_url(), self.journal_two.site_url()], - cms_logic.excluded_urls() + cms_logic.excluded_urls(), ) - @patch('cms.logic.get_base') - @patch('cms.logic.excluded_urls') + @patch("cms.logic.get_base") + @patch("cms.logic.excluded_urls") def test_url_in_scope(self, excluded_urls, get_base): - get_base.return_value = 'https://www.openlibhums.org' - excluded_urls.return_value = ['https://www.openlibhums.org/journal1'] + get_base.return_value = "https://www.openlibhums.org" + excluded_urls.return_value = ["https://www.openlibhums.org/journal1"] self.assertTrue( cms_logic.url_in_scope( self.press, - 'https://www.openlibhums.org/my-site-page/', + "https://www.openlibhums.org/my-site-page/", ), ) self.assertFalse( cms_logic.url_in_scope( self.press, - 'https://www.openlibhums.org/journal1/a/', + "https://www.openlibhums.org/journal1/a/", ) ) self.assertFalse( - cms_logic.url_in_scope( - self.press, - 'https://www.wikipedia.org' - ) + cms_logic.url_in_scope(self.press, "https://www.wikipedia.org") ) - @patch('cms.logic.update_search_data') + @patch("cms.logic.update_search_data") def test_generate_command(self, update_search_data): update_search_data.return_value = [] - call_command('generate_site_search_data', '--press_id', self.press.pk) + call_command("generate_site_search_data", "--press_id", self.press.pk) update_search_data.assert_called() def test_delete_command(self): docs_path = os.path.join( settings.MEDIA_ROOT, - 'press', + "press", self.docs_label, ) - call_command('generate_site_search_data', '--press_id', self.press.pk) + call_command("generate_site_search_data", "--press_id", self.press.pk) self.assertTrue(os.path.exists(docs_path)) - call_command('delete_site_search_data', '--press_id', self.press.pk) + call_command("delete_site_search_data", "--press_id", self.press.pk) self.assertFalse(os.path.exists(docs_path)) self.assertFalse(os.path.exists(cms_logic.SITE_SEARCH_PATH)) - @patch('cms.logic.get_press_site_search_data') + @patch("cms.logic.get_press_site_search_data") def test_update_search_data_new(self, get_press_site_search_data): get_press_site_search_data.return_value = [ - {'test': 'test value', 'other': 'other value'} + {"test": "test value", "other": "other value"} ] cms_logic.update_search_data(self.press) docs_file = cms_models.MediaFile.objects.get(label=self.docs_label) self.assertTrue(os.path.exists(docs_file.file.path)) - @patch('cms.logic.get_press_site_search_data') + @patch("cms.logic.get_press_site_search_data") def test_update_search_data_existing(self, get_press_site_search_data): - get_press_site_search_data.return_value = [ - {'old': ''} - ] + get_press_site_search_data.return_value = [{"old": ""}] cms_logic.update_search_data(self.press) - get_press_site_search_data.return_value = [ - {'new': ''} - ] + get_press_site_search_data.return_value = [{"new": ""}] cms_logic.update_search_data(self.press) get_press_site_search_data.assert_called() media_file = cms_models.MediaFile.objects.get(label=self.docs_label) - with open(media_file.file.path, 'r') as mf: + with open(media_file.file.path, "r") as mf: s = mf.read() - self.assertTrue('old' not in s) - self.assertTrue('new' in s) + self.assertTrue("old" not in s) + self.assertTrue("new" in s) diff --git a/src/cms/tests/test_views.py b/src/cms/tests/test_views.py index c30e31e6a8..2f2286692e 100644 --- a/src/cms/tests/test_views.py +++ b/src/cms/tests/test_views.py @@ -12,62 +12,57 @@ class ViewTests(test_models.TestCaseWithCMSData): - - @patch('cms.views.render') + @patch("cms.views.render") def test_view_page_journal(self, render): - cms_views.view_page(self.request_journal, 'test-name') + cms_views.view_page(self.request_journal, "test-name") render.assert_called_with( - self.request_journal, - 'cms/page.html', - {'page': self.journal_cms_page} + self.request_journal, "cms/page.html", {"page": self.journal_cms_page} ) - @patch('cms.views.render') + @patch("cms.views.render") def test_view_page_press(self, render): - cms_views.view_page(self.request_press, 'test-name') + cms_views.view_page(self.request_press, "test-name") render.assert_called_with( - self.request_press, - 'press/cms/page.html', - {'page': self.press_cms_page} + self.request_press, "press/cms/page.html", {"page": self.press_cms_page} ) - @patch('os.path.exists') - @patch('cms.logic.get_custom_templates_path') - @patch('cms.views.render') + @patch("os.path.exists") + @patch("cms.logic.get_custom_templates_path") + @patch("cms.views.render") def test_view_page_press_custom_template(self, render, get_path, path_exists): - get_path.return_value = 'fake/path/to/custom' + get_path.return_value = "fake/path/to/custom" path_exists.return_value = True - self.press_cms_page.template = 'custom/my_template.html' + self.press_cms_page.template = "custom/my_template.html" self.press_cms_page.save() - cms_views.view_page(self.request_press, 'test-name') + cms_views.view_page(self.request_press, "test-name") render.assert_called_with( - self.request_press, - 'custom/my_template.html', - {'page': self.press_cms_page} + self.request_press, "custom/my_template.html", {"page": self.press_cms_page} ) - self.press_cms_page.template = '' + self.press_cms_page.template = "" self.press_cms_page.save() - @patch('cms.logic.get_custom_templates_path') - @patch('cms.views.render') + @patch("cms.logic.get_custom_templates_path") + @patch("cms.views.render") def test_view_page_no_custom_folder_set_raises_error(self, _render, get_path): - self.press_cms_page.template = 'custom/my_template.html' + self.press_cms_page.template = "custom/my_template.html" self.press_cms_page.save() - get_path.return_value = '' + get_path.return_value = "" with self.assertRaises(Http404): - cms_views.view_page(self.request_press, 'test-name') - self.press_cms_page.template = '' + cms_views.view_page(self.request_press, "test-name") + self.press_cms_page.template = "" self.press_cms_page.save() - @patch('os.path.exists') - @patch('cms.logic.get_custom_templates_path') - @patch('cms.views.render') - def test_view_page_bad_custom_template_path_raises_error(self, _render, get_path, path_exists): - self.press_cms_page.template = 'custom/my_template.html' + @patch("os.path.exists") + @patch("cms.logic.get_custom_templates_path") + @patch("cms.views.render") + def test_view_page_bad_custom_template_path_raises_error( + self, _render, get_path, path_exists + ): + self.press_cms_page.template = "custom/my_template.html" self.press_cms_page.save() - get_path.return_value = 'fake/path/to/custom' + get_path.return_value = "fake/path/to/custom" path_exists.return_value = False with self.assertRaises(Http404): - cms_views.view_page(self.request_press, 'test-name') - self.press_cms_page.template = '' + cms_views.view_page(self.request_press, "test-name") + self.press_cms_page.template = "" self.press_cms_page.save() diff --git a/src/cms/translation.py b/src/cms/translation.py index d304bdbced..5b3b5d64d3 100644 --- a/src/cms/translation.py +++ b/src/cms/translation.py @@ -6,7 +6,7 @@ @register(models.Page) class PageTranslationOptions(TranslationOptions): - fields = ('display_name', 'content') + fields = ("display_name", "content") # This adds a history field to models.Page @@ -15,9 +15,9 @@ class PageTranslationOptions(TranslationOptions): @register(models.NavigationItem) class NavigationItemTranslationOptions(TranslationOptions): - fields = ('link_name',) + fields = ("link_name",) @register(models.SubmissionItem) class SubmissionItemTranslationOption(TranslationOptions): - fields = ('title', 'text') + fields = ("title", "text") diff --git a/src/cms/urls.py b/src/cms/urls.py index c7a831139a..96fbe36b19 100755 --- a/src/cms/urls.py +++ b/src/cms/urls.py @@ -10,33 +10,38 @@ urlpatterns = [ # Probably needs some multi-journal logic here - re_path(r'^$', views.index, name='cms_index'), - re_path(r'^page/new/$', views.page_manage, name='cms_page_new'), - re_path(r'^page/(?P\d+)/$', views.page_manage, name='cms_page_edit'), - re_path(r'^(?Pw+?)$', views.view_page, name='cms_page'), - - re_path(r'^nav/$', views.nav, name='cms_nav'), - re_path(r'^nav/(?P\d+)/$', views.nav, name='cms_nav_edit'), - - re_path(r'^submission_items/$', views.submission_items, name='cms_submission_items'), - re_path(r'^submission_items/add/$', + re_path(r"^$", views.index, name="cms_index"), + re_path(r"^page/new/$", views.page_manage, name="cms_page_new"), + re_path(r"^page/(?P\d+)/$", views.page_manage, name="cms_page_edit"), + re_path(r"^(?Pw+?)$", views.view_page, name="cms_page"), + re_path(r"^nav/$", views.nav, name="cms_nav"), + re_path(r"^nav/(?P\d+)/$", views.nav, name="cms_nav_edit"), + re_path( + r"^submission_items/$", views.submission_items, name="cms_submission_items" + ), + re_path( + r"^submission_items/add/$", views.order_submission_items, - name='cms_order_submission_items', - ), - re_path(r'^submission_items/order/$', + name="cms_order_submission_items", + ), + re_path( + r"^submission_items/order/$", views.edit_or_create_submission_item, - name='cms_add_submission_item', - ), - re_path(r'^submission_items/(?P\d+)/$', + name="cms_add_submission_item", + ), + re_path( + r"^submission_items/(?P\d+)/$", views.edit_or_create_submission_item, - name='cms_edit_submission_item', - ), - re_path(r'^media_files/$', + name="cms_edit_submission_item", + ), + re_path( + r"^media_files/$", views.file_list, - name='cms_file_list', - ), - re_path(r'^media_files/upload/$', + name="cms_file_list", + ), + re_path( + r"^media_files/upload/$", views.file_upload, - name='cms_file_upload', - ), + name="cms_file_upload", + ), ] diff --git a/src/cms/views.py b/src/cms/views.py index c5d466fe3e..1dccea796d 100755 --- a/src/cms/views.py +++ b/src/cms/views.py @@ -7,13 +7,7 @@ from django.contrib import messages from django.db.models import Q -from django.shortcuts import ( - render, - get_object_or_404, - redirect, - HttpResponse, - Http404 -) +from django.shortcuts import render, get_object_or_404, redirect, HttpResponse, Http404 from django.urls import reverse from django.utils import translation from django.views.decorators.http import require_POST @@ -51,14 +45,15 @@ def index(request): collection_nav_items = None if request.journal: collection_nav_items = models.NavigationItem.get_issue_types_for_nav( - request.journal) + request.journal + ) xsl_form = XSLFileForm() xsl_files = core_models.XSLFile.objects.filter( - Q(journal=request.journal)|Q(journal__isnull=True) + Q(journal=request.journal) | Q(journal__isnull=True) ) - if request.POST and 'delete' in request.POST: - page_id = request.POST.get('delete') + if request.POST and "delete" in request.POST: + page_id = request.POST.get("delete") page = get_object_or_404( models.Page, pk=page_id, @@ -66,40 +61,40 @@ def index(request): object_id=request.site_type.pk, ) page.delete() - return redirect(reverse('cms_index')) + return redirect(reverse("cms_index")) - if request.POST and 'new_xsl' in request.POST: + if request.POST and "new_xsl" in request.POST: xsl_form = XSLFileForm(request.POST, request.FILES) if xsl_form.is_valid(): xsl_form.save() messages.add_message(request, messages.INFO, "XSLT file has been uploaded.") else: messages.add_message( - request, messages.ERROR, - "Please correct the errors on the form and try again" + request, + messages.ERROR, + "Please correct the errors on the form and try again", ) - if 'clear' in request.POST: + if "clear" in request.POST: files.unlink_journal_file(request, file=None, xslt=True) request.journal.has_xslt = False request.journal.save() - elif request.POST and 'change_xsl' in request.POST: - xsl_file = get_object_or_404(core_models.XSLFile, - pk=request.POST["change_xsl"]) + elif request.POST and "change_xsl" in request.POST: + xsl_file = get_object_or_404(core_models.XSLFile, pk=request.POST["change_xsl"]) request.journal.xsl = xsl_file request.journal.save() - return redirect(reverse('cms_index')) + return redirect(reverse("cms_index")) - template = 'cms/index.html' + template = "cms/index.html" context = { - 'journal': request.journal, - 'pages': pages, - 'top_nav_items': top_nav_items, - 'collection_nav_items': collection_nav_items, - 'xsl_form': xsl_form, - 'xsl_files': xsl_files, + "journal": request.journal, + "pages": pages, + "top_nav_items": top_nav_items, + "collection_nav_items": collection_nav_items, + "xsl_form": xsl_form, + "xsl_files": xsl_files, } return render(request, template, context) @@ -116,37 +111,34 @@ def view_page(request, page_name): models.Page, name=page_name, content_type=request.model_content_type, - object_id=request.site_type.pk + object_id=request.site_type.pk, ) if page.template: - templates_path = logic.get_custom_templates_path( - request.journal, - request.press - ) + templates_path = logic.get_custom_templates_path(request.journal, request.press) if not templates_path: logger.error( - f'No custom template folder has been set ' - f'but CMS page {page.pk} expects to find one.' + f"No custom template folder has been set " + f"but CMS page {page.pk} expects to find one." ) raise Http404 elif not os.path.exists( os.path.join(templates_path, os.path.basename(page.template)) ): logger.error( - f'CMS page {page.pk} is set to use ' - f'nonexistent template {page.template}.' + f"CMS page {page.pk} is set to use " + f"nonexistent template {page.template}." ) raise Http404 else: template = page.template elif request.journal: - template = 'cms/page.html' + template = "cms/page.html" else: - template = 'press/cms/page.html' + template = "press/cms/page.html" context = { - 'page': page, + "page": page, } return render(request, template, context) @@ -163,8 +155,12 @@ def page_manage(request, page_id=None): """ with translation.override(request.override_language): if page_id: - page = get_object_or_404(models.Page, pk=page_id, - content_type=request.model_content_type, object_id=request.site_type.pk) + page = get_object_or_404( + models.Page, + pk=page_id, + content_type=request.model_content_type, + object_id=request.site_type.pk, + ) page_form = forms.PageForm(instance=page) edit = True else: @@ -173,7 +169,6 @@ def page_manage(request, page_id=None): edit = False if request.POST: - if page_id: page_form = forms.PageForm(request.POST, instance=page) else: @@ -185,18 +180,18 @@ def page_manage(request, page_id=None): page.object_id = request.site_type.pk page.save() - messages.add_message(request, messages.INFO, 'Page saved.') + messages.add_message(request, messages.INFO, "Page saved.") return language_override_redirect( request, - 'cms_page_edit', - {'page_id': page.pk}, + "cms_page_edit", + {"page_id": page.pk}, ) - template = 'cms/page_manage.html' + template = "cms/page_manage.html" context = { - 'page': page, - 'form': page_form, - 'edit': edit, + "page": page, + "form": page_form, + "edit": edit, } return render(request, template, context) @@ -228,11 +223,11 @@ def nav(request, nav_id=None): request.journal, ) - if request.POST.get('nav'): - attr = request.POST.get('nav') + if request.POST.get("nav"): + attr = request.POST.get("nav") setattr(request.journal, attr, not getattr(request.journal, attr)) request.journal.save() - return redirect(reverse('cms_nav')) + return redirect(reverse("cms_nav")) elif "editorial_team" in request.POST: setting_handler.toggle_boolean_setting( @@ -241,7 +236,7 @@ def nav(request, nav_id=None): journal=request.journal, ) - elif 'keyword_list_page' in request.POST: + elif "keyword_list_page" in request.POST: setting_handler.toggle_boolean_setting( setting_group_name="general", setting_name="keyword_list_page", @@ -264,9 +259,11 @@ def nav(request, nav_id=None): ) models.NavigationItem.toggle_collection_nav(issue_type) - if request.POST and 'edit_nav' in request.POST: + if request.POST and "edit_nav" in request.POST: form = forms.NavForm( - request.POST, request=request, instance=nav_to_edit, + request.POST, + request=request, + instance=nav_to_edit, ) if form.is_valid(): @@ -278,28 +275,28 @@ def nav(request, nav_id=None): messages.add_message( request, messages.SUCCESS, - 'Nav Item Saved.', + "Nav Item Saved.", ) return language_override_redirect( request, - 'cms_nav_edit', - {'nav_id': new_nav_item.pk}, + "cms_nav_edit", + {"nav_id": new_nav_item.pk}, ) - template = 'cms/nav.html' + template = "cms/nav.html" context = { - 'form': form, - 'top_nav_items': top_nav_items, - 'collection_nav_items': collection_nav_items, + "form": form, + "top_nav_items": top_nav_items, + "collection_nav_items": collection_nav_items, } if request.journal: - context['keyword_list_page'] = request.journal.get_setting( + context["keyword_list_page"] = request.journal.get_setting( "general", "keyword_list_page", ) - context['enable_editorial_display'] = request.journal.get_setting( + context["enable_editorial_display"] = request.journal.get_setting( "general", "enable_editorial_display", ) @@ -315,8 +312,8 @@ def submission_items(request): item_list = models.SubmissionItem.objects.filter( journal=request.journal, ) - if request.POST and 'delete' in request.POST: - item_id = request.POST.get('delete') + if request.POST and "delete" in request.POST: + item_id = request.POST.get("delete") try: models.SubmissionItem.objects.get( pk=item_id, @@ -325,21 +322,21 @@ def submission_items(request): messages.add_message( request, messages.SUCCESS, - 'Item deleted.', + "Item deleted.", ) except models.SubmissionItem.DoesNotExist: messages.add_message( request, messages.ERROR, - 'No matching Submission Item found.', + "No matching Submission Item found.", ) return redirect( - reverse('cms_submission_items'), + reverse("cms_submission_items"), ) - template = 'admin/cms/submission_item_list.html' + template = "admin/cms/submission_item_list.html" context = { - 'item_list': item_list, + "item_list": item_list, } return render(request, template, context) @@ -363,27 +360,23 @@ def edit_or_create_submission_item(request, item_id=None): ) if request.POST: form = forms.SubmissionItemForm( - request.POST, - instance=item, - journal=request.journal + request.POST, instance=item, journal=request.journal ) if form.is_valid(): saved_item = form.save() messages.add_message( request, messages.SUCCESS, - 'New item created.' if not item else 'Item updated.' + "New item created." if not item else "Item updated.", ) return language_override_redirect( - request, - 'cms_edit_submission_item', - {'item_id': saved_item.pk} + request, "cms_edit_submission_item", {"item_id": saved_item.pk} ) - template = 'admin/cms/submission_item_form.html' + template = "admin/cms/submission_item_form.html" context = { - 'form': form, - 'item': item, + "form": form, + "item": item, } return render(request, template, context) @@ -395,11 +388,9 @@ def order_submission_items(request): journal=request.journal, ) set_order( - items, - 'order', - [int(item_pk) for item_pk in request.POST.getlist('item[]')] + items, "order", [int(item_pk) for item_pk in request.POST.getlist("item[]")] ) - return HttpResponse('Ok') + return HttpResponse("Ok") @editor_user_required @@ -411,8 +402,8 @@ def file_list(request): journal=request.journal, ) - if request.POST and 'delete' in request.POST: - id_to_delete = request.POST.get('delete') + if request.POST and "delete" in request.POST: + id_to_delete = request.POST.get("delete") media_file = get_object_or_404( models.MediaFile, pk=id_to_delete, @@ -422,14 +413,14 @@ def file_list(request): media_file.delete() return redirect( reverse( - 'cms_file_list', + "cms_file_list", ) ) - template = 'admin/cms/media_files.html' + template = "admin/cms/media_files.html" context = { - 'media_files': media_files, - 'form': form, + "media_files": media_files, + "form": form, } return render( request, @@ -459,6 +450,6 @@ def file_upload(request): ) return redirect( reverse( - 'cms_file_list', + "cms_file_list", ) ) diff --git a/src/comms/admin.py b/src/comms/admin.py index c97c684086..fe388c1d67 100755 --- a/src/comms/admin.py +++ b/src/comms/admin.py @@ -10,8 +10,8 @@ class TagAdmin(admin.ModelAdmin): - list_display = ('text', '_count') - search_fields = ('text',) + list_display = ("text", "_count") + search_fields = ("text",) def _count(self, obj): return obj.tags.count() @@ -22,17 +22,33 @@ def _count(self, obj): class NewsItemAdmin(SimpleHistoryAdmin): - list_display = ('title', 'posted', 'posted_by', - 'start_display', 'end_display', 'object') - list_filter = (admin_utils.GenericRelationJournalFilter, - admin_utils.GenericRelationPressFilter, - 'posted', 'start_display', 'end_display') - search_fields = ('title', 'body', 'tags__text', 'posted_by__email', - 'posted_by__first_name', 'posted_by__last_name', - 'custom_byline') - date_hierarchy = ('posted') - filter_horizontal = ('tags',) - raw_id_fields = ('posted_by', 'large_image_file') + list_display = ( + "title", + "posted", + "posted_by", + "start_display", + "end_display", + "object", + ) + list_filter = ( + admin_utils.GenericRelationJournalFilter, + admin_utils.GenericRelationPressFilter, + "posted", + "start_display", + "end_display", + ) + search_fields = ( + "title", + "body", + "tags__text", + "posted_by__email", + "posted_by__first_name", + "posted_by__last_name", + "custom_byline", + ) + date_hierarchy = "posted" + filter_horizontal = ("tags",) + raw_id_fields = ("posted_by", "large_image_file") admin_list = [ diff --git a/src/comms/apps.py b/src/comms/apps.py index 412b60afbf..9ab221f5de 100755 --- a/src/comms/apps.py +++ b/src/comms/apps.py @@ -2,4 +2,4 @@ class CommsConfig(AppConfig): - name = 'comms' + name = "comms" diff --git a/src/comms/forms.py b/src/comms/forms.py index d09ffb01bf..6e169bfbbe 100755 --- a/src/comms/forms.py +++ b/src/comms/forms.py @@ -5,13 +5,12 @@ class NewsItemForm(forms.ModelForm): - image_file = forms.FileField(required=False) tags = forms.CharField(required=False) def save(self, commit=True): news_item = super(NewsItemForm, self).save() - posted_tags = self.cleaned_data['tags'].split(',') + posted_tags = self.cleaned_data["tags"].split(",") news_item.set_tags(posted_tags=posted_tags) news_item.save() @@ -20,14 +19,14 @@ def save(self, commit=True): class Meta: model = models.NewsItem exclude = ( - 'content_type', - 'object_id', - 'posted', - 'posted_by', - 'large_image_file', - 'tags', + "content_type", + "object_id", + "posted", + "posted_by", + "large_image_file", + "tags", ) widgets = { - 'start_display': HTMLDateInput, - 'end_display': HTMLDateInput, + "start_display": HTMLDateInput, + "end_display": HTMLDateInput, } diff --git a/src/comms/migrations/0001_initial.py b/src/comms/migrations/0001_initial.py index be80c36f1b..940d5fd720 100755 --- a/src/comms/migrations/0001_initial.py +++ b/src/comms/migrations/0001_initial.py @@ -9,32 +9,64 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), + ("contenttypes", "0002_remove_content_type_name"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='NewsItem', + name="NewsItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('title', models.CharField(max_length=500)), - ('body', models.TextField()), - ('posted', models.DateTimeField(default=django.utils.timezone.now)), - ('start_display', models.DateField(default=django.utils.timezone.now)), - ('end_display', models.DateField(blank=True, null=True)), - ('sequence', models.PositiveIntegerField(default=0)), - ('content_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='news_content_type', to='contenttypes.ContentType')), - ('large_image_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='news_file', to='core.File')), - ('posted_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("title", models.CharField(max_length=500)), + ("body", models.TextField()), + ("posted", models.DateTimeField(default=django.utils.timezone.now)), + ("start_display", models.DateField(default=django.utils.timezone.now)), + ("end_display", models.DateField(blank=True, null=True)), + ("sequence", models.PositiveIntegerField(default=0)), + ( + "content_type", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="news_content_type", + to="contenttypes.ContentType", + ), + ), + ( + "large_image_file", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="news_file", + to="core.File", + ), + ), + ( + "posted_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('-posted', 'title'), + "ordering": ("-posted", "title"), }, ), ] diff --git a/src/comms/migrations/0002_auto_20170816_1050.py b/src/comms/migrations/0002_auto_20170816_1050.py index 66753bd8c5..5bc7647795 100755 --- a/src/comms/migrations/0002_auto_20170816_1050.py +++ b/src/comms/migrations/0002_auto_20170816_1050.py @@ -7,27 +7,40 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0001_initial'), + ("comms", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Tag', + name="Tag", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.CharField(max_length=255)), ], ), migrations.AlterField( - model_name='newsitem', - name='large_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='large_news_file', to='core.File'), + model_name="newsitem", + name="large_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="large_news_file", + to="core.File", + ), ), migrations.AddField( - model_name='newsitem', - name='tags', - field=models.ManyToManyField(related_name='tags', to='comms.Tag'), + model_name="newsitem", + name="tags", + field=models.ManyToManyField(related_name="tags", to="comms.Tag"), ), ] diff --git a/src/comms/migrations/0003_newsitem_custom_byline.py b/src/comms/migrations/0003_newsitem_custom_byline.py index ff853e1a62..83ad6b81fe 100644 --- a/src/comms/migrations/0003_newsitem_custom_byline.py +++ b/src/comms/migrations/0003_newsitem_custom_byline.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0002_auto_20170816_1050'), + ("comms", "0002_auto_20170816_1050"), ] operations = [ migrations.AddField( - model_name='newsitem', - name='custom_byline', - field=models.CharField(blank=True, help_text='If you want a custom byline add it here. This will overwrite the display of the user who created the news item with whatever text is added here.', max_length=255, null=True), + model_name="newsitem", + name="custom_byline", + field=models.CharField( + blank=True, + help_text="If you want a custom byline add it here. This will overwrite the display of the user who created the news item with whatever text is added here.", + max_length=255, + null=True, + ), ), ] diff --git a/src/comms/migrations/0004_auto_20230223_1328.py b/src/comms/migrations/0004_auto_20230223_1328.py index 4918f736bb..aced0a1851 100644 --- a/src/comms/migrations/0004_auto_20230223_1328.py +++ b/src/comms/migrations/0004_auto_20230223_1328.py @@ -7,24 +7,33 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0003_newsitem_custom_byline'), + ("comms", "0003_newsitem_custom_byline"), ] operations = [ migrations.AlterModelOptions( - name='newsitem', - options={'ordering': ('pinned', '-posted', 'title')}, + name="newsitem", + options={"ordering": ("pinned", "-posted", "title")}, ), migrations.AddField( - model_name='newsitem', - name='pinned', - field=models.BooleanField(default=False, help_text='Pinned news items will appear at the top of the news list'), + model_name="newsitem", + name="pinned", + field=models.BooleanField( + default=False, + help_text="Pinned news items will appear at the top of the news list", + ), ), migrations.AlterField( - model_name='newsitem', - name='large_image_file', - field=models.ForeignKey(blank=True, help_text='An image for the top of the news item page and the news list page. Note that it will be automatically cropped to 750px x 324px, so wide images work best.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='large_news_file', to='core.File'), + model_name="newsitem", + name="large_image_file", + field=models.ForeignKey( + blank=True, + help_text="An image for the top of the news item page and the news list page. Note that it will be automatically cropped to 750px x 324px, so wide images work best.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="large_news_file", + to="core.File", + ), ), ] diff --git a/src/comms/migrations/0004_newsitem_support_copy_paste.py b/src/comms/migrations/0004_newsitem_support_copy_paste.py index 1be1e72cbd..767f9bf770 100644 --- a/src/comms/migrations/0004_newsitem_support_copy_paste.py +++ b/src/comms/migrations/0004_newsitem_support_copy_paste.py @@ -4,9 +4,8 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0003_newsitem_custom_byline'), + ("comms", "0003_newsitem_custom_byline"), ] # Why two operations? @@ -18,25 +17,25 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( - model_name='newsitem', - name='support_copy_paste', + model_name="newsitem", + name="support_copy_paste", field=models.BooleanField( default=False, ), ), migrations.AlterField( - model_name='newsitem', - name='support_copy_paste', + model_name="newsitem", + name="support_copy_paste", field=models.BooleanField( default=True, - help_text='Turn this on if copy-pasting content ' - 'from a word processor, ' - 'or using the toolbar to format text. ' - 'It tells Janeway to clear out formatting ' - 'that does not play nice. ' - 'Turn it off and leave it off if anyone has ' - 'added custom HTML or CSS using the code view, ' - 'since it might remove custom code.', + help_text="Turn this on if copy-pasting content " + "from a word processor, " + "or using the toolbar to format text. " + "It tells Janeway to clear out formatting " + "that does not play nice. " + "Turn it off and leave it off if anyone has " + "added custom HTML or CSS using the code view, " + "since it might remove custom code.", ), ), ] diff --git a/src/comms/migrations/0005_historicalnewsitem.py b/src/comms/migrations/0005_historicalnewsitem.py index fac46e78f0..f9644cf684 100644 --- a/src/comms/migrations/0005_historicalnewsitem.py +++ b/src/comms/migrations/0005_historicalnewsitem.py @@ -8,54 +8,107 @@ class Migration(migrations.Migration): - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), + ("contenttypes", "0002_remove_content_type_name"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0081_alter_account_preferred_timezone'), - ('comms', '0004_newsitem_support_copy_paste'), + ("core", "0081_alter_account_preferred_timezone"), + ("comms", "0004_newsitem_support_copy_paste"), ] operations = [ migrations.CreateModel( - name='HistoricalNewsItem', + name="HistoricalNewsItem", fields=[ - ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), - ('support_copy_paste', models.BooleanField(default=True, help_text='Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('title', models.CharField(max_length=500)), - ('body', models.TextField()), - ('posted', models.DateTimeField(default=django.utils.timezone.now)), - ('start_display', models.DateField(default=django.utils.timezone.now)), - ('end_display', models.DateField(blank=True, null=True)), - ('sequence', models.PositiveIntegerField(default=0)), - ('custom_byline', models.CharField(blank=True, help_text='If you want a custom byline add it here. This will overwrite the display of the user who created the news item with whatever text is added here.', max_length=255, null=True)), - ('history_id', models.AutoField(primary_key=True, serialize=False)), - ('history_date', models.DateTimeField(db_index=True)), - ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('content_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='contenttypes.contenttype')), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ( - 'large_image_file', + ( + "id", + models.IntegerField( + auto_created=True, blank=True, db_index=True, verbose_name="ID" + ), + ), + ( + "support_copy_paste", + models.BooleanField( + default=True, + help_text="Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("title", models.CharField(max_length=500)), + ("body", models.TextField()), + ("posted", models.DateTimeField(default=django.utils.timezone.now)), + ("start_display", models.DateField(default=django.utils.timezone.now)), + ("end_display", models.DateField(blank=True, null=True)), + ("sequence", models.PositiveIntegerField(default=0)), + ( + "custom_byline", + models.CharField( + blank=True, + help_text="If you want a custom byline add it here. This will overwrite the display of the user who created the news item with whatever text is added here.", + max_length=255, + null=True, + ), + ), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ( + "history_type", + models.CharField( + choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], + max_length=1, + ), + ), + ( + "content_type", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "history_user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "large_image_file", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + help_text="An image for the top of the news item page and the " + "news list page. Note that it will be automatically cropped to " + "750px x 324px, so wide images work best.", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="core.file", + ), + ), + ( + "posted_by", models.ForeignKey( blank=True, db_constraint=False, null=True, - help_text='An image for the top of the news item page and the ' - 'news list page. Note that it will be automatically cropped to ' - '750px x 324px, so wide images work best.', on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', - to='core.file' - )), - ('posted_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'historical news item', - 'verbose_name_plural': 'historical news items', - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': ('history_date', 'history_id'), + "verbose_name": "historical news item", + "verbose_name_plural": "historical news items", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), }, bases=(simple_history.models.HistoricalChanges, models.Model), ), diff --git a/src/comms/migrations/0006_auto_20240312_0922.py b/src/comms/migrations/0006_auto_20240312_0922.py index 7b74d13e40..302fcc53fc 100644 --- a/src/comms/migrations/0006_auto_20240312_0922.py +++ b/src/comms/migrations/0006_auto_20240312_0922.py @@ -5,28 +5,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0005_historicalnewsitem'), + ("comms", "0005_historicalnewsitem"), ] operations = [ migrations.RemoveField( - model_name='historicalnewsitem', - name='support_copy_paste', + model_name="historicalnewsitem", + name="support_copy_paste", ), migrations.RemoveField( - model_name='newsitem', - name='support_copy_paste', + model_name="newsitem", + name="support_copy_paste", ), migrations.AlterField( - model_name='historicalnewsitem', - name='body', + model_name="historicalnewsitem", + name="body", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='newsitem', - name='body', + model_name="newsitem", + name="body", field=core.model_utils.JanewayBleachField(), ), ] diff --git a/src/comms/migrations/0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem.py b/src/comms/migrations/0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem.py index 9aba3b3a37..c62ff74d05 100644 --- a/src/comms/migrations/0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem.py +++ b/src/comms/migrations/0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0004_auto_20230223_1328'), - ('comms', '0005_historicalnewsitem'), + ("comms", "0004_auto_20230223_1328"), + ("comms", "0005_historicalnewsitem"), ] - operations = [ - ] + operations = [] diff --git a/src/comms/migrations/0007_news_item_pinned_20240206_0855.py b/src/comms/migrations/0007_news_item_pinned_20240206_0855.py index d113d5d6e2..8af1607f6a 100644 --- a/src/comms/migrations/0007_news_item_pinned_20240206_0855.py +++ b/src/comms/migrations/0007_news_item_pinned_20240206_0855.py @@ -5,19 +5,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem'), + ("comms", "0006_merge_0004_auto_20230223_1328_0005_historicalnewsitem"), ] operations = [ migrations.AddField( - model_name='historicalnewsitem', - name='pinned', + model_name="historicalnewsitem", + name="pinned", field=models.BooleanField( default=False, - help_text='Pinned news items will appear at the top of the news ' - 'list', + help_text="Pinned news items will appear at the top of the news " + "list", ), ), ] diff --git a/src/comms/migrations/0008_merge_20240315_1650.py b/src/comms/migrations/0008_merge_20240315_1650.py index 64a3112664..38d522349a 100644 --- a/src/comms/migrations/0008_merge_20240315_1650.py +++ b/src/comms/migrations/0008_merge_20240315_1650.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0006_auto_20240312_0922'), - ('comms', '0007_news_item_pinned_20240206_0855'), + ("comms", "0006_auto_20240312_0922"), + ("comms", "0007_news_item_pinned_20240206_0855"), ] - operations = [ - ] + operations = [] diff --git a/src/comms/migrations/0009_auto_20240415_1302.py b/src/comms/migrations/0009_auto_20240415_1302.py index 318e5f86a6..910e0ce46a 100644 --- a/src/comms/migrations/0009_auto_20240415_1302.py +++ b/src/comms/migrations/0009_auto_20240415_1302.py @@ -5,20 +5,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0008_merge_20240315_1650'), + ("comms", "0008_merge_20240315_1650"), ] operations = [ migrations.AlterField( - model_name='historicalnewsitem', - name='title', + model_name="historicalnewsitem", + name="title", field=core.model_utils.JanewayBleachCharField(), ), migrations.AlterField( - model_name='newsitem', - name='title', + model_name="newsitem", + name="title", field=core.model_utils.JanewayBleachCharField(), ), ] diff --git a/src/comms/models.py b/src/comms/models.py index 10d51630b4..2dcaa558c9 100755 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -19,60 +19,77 @@ class ActiveNewsItemManager(models.Manager): def get_queryset(self): - return super().get_queryset().filter( - (models.Q(start_display__lte=timezone.now()) | models.Q(start_display=None)) - & (models.Q(end_display__gte=timezone.now()) | models.Q(end_display=None)) + return ( + super() + .get_queryset() + .filter( + ( + models.Q(start_display__lte=timezone.now()) + | models.Q(start_display=None) + ) + & ( + models.Q(end_display__gte=timezone.now()) + | models.Q(end_display=None) + ) + ) ) class NewsItem(models.Model): - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='news_content_type', null=True) + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + related_name="news_content_type", + null=True, + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") title = JanewayBleachCharField() body = JanewayBleachField() posted = models.DateTimeField(default=timezone.now) - posted_by = models.ForeignKey('core.Account', blank=True, null=True, on_delete=models.SET_NULL) + posted_by = models.ForeignKey( + "core.Account", blank=True, null=True, on_delete=models.SET_NULL + ) start_display = models.DateField(default=timezone.now) end_display = models.DateField(blank=True, null=True) sequence = models.PositiveIntegerField(default=0) large_image_file = models.ForeignKey( - 'core.File', + "core.File", null=True, blank=True, - related_name='large_news_file', + related_name="large_news_file", on_delete=models.SET_NULL, - help_text='An image for the top of the news item page and the ' - 'news list page. Note that it will be automatically ' - 'cropped to 750px x 324px, so wide images work best.', + help_text="An image for the top of the news item page and the " + "news list page. Note that it will be automatically " + "cropped to 750px x 324px, so wide images work best.", ) - tags = models.ManyToManyField('Tag', related_name='tags') + tags = models.ManyToManyField("Tag", related_name="tags") custom_byline = models.CharField( max_length=255, blank=True, null=True, help_text="If you want a custom byline add it here. This will overwrite the display of the user who created " - "the news item with whatever text is added here.", + "the news item with whatever text is added here.", ) history = HistoricalRecords() pinned = models.BooleanField( default=False, - help_text="Pinned news items will appear at the top of the news list" + help_text="Pinned news items will appear at the top of the news list", ) objects = models.Manager() active_objects = ActiveNewsItemManager() class Meta: - ordering = ('pinned', '-posted', 'title') + ordering = ("pinned", "-posted", "title") @property def url(self): - path = reverse('core_news_item', kwargs={'news_pk': self.pk}) + path = reverse("core_news_item", kwargs={"news_pk": self.pk}) return self.object.site_url(path) @property @@ -89,15 +106,19 @@ def carousel_title(self): @property def carousel_image_resolver(self): - return 'news_file_download' + return "news_file_download" def serve_news_file(self): if self.large_image_file: - if self.content_type.name == 'press': - return files.serve_file_to_browser(self.large_image_file.press_path(), self.large_image_file) + if self.content_type.name == "press": + return files.serve_file_to_browser( + self.large_image_file.press_path(), self.large_image_file + ) else: - return files.serve_file_to_browser(self.large_image_file.journal_path(self.object), - self.large_image_file) + return files.serve_file_to_browser( + self.large_image_file.journal_path(self.object), + self.large_image_file, + ) else: return Http404 @@ -105,7 +126,7 @@ def set_tags(self, posted_tags): str_tags = [tag.text for tag in self.tags.all()] for tag in posted_tags: - if tag not in str_tags and tag != '': + if tag not in str_tags and tag != "": new_tag, c = Tag.objects.get_or_create(text=tag) self.tags.add(new_tag) @@ -116,8 +137,8 @@ def set_tags(self, posted_tags): def byline(self): if self.custom_byline: - return _('Posted by {byline}').format(byline=self.custom_byline) - return _('Posted by {byline}').format(byline=self.posted_by.full_name()) + return _("Posted by {byline}").format(byline=self.custom_byline) + return _("Posted by {byline}").format(byline=self.posted_by.full_name()) def best_image_url(self): """ @@ -130,16 +151,16 @@ def best_image_url(self): path = None if self.large_image_file: path = reverse( - 'news_file_download', + "news_file_download", kwargs={ - 'identifier_type': 'id', - 'identifier': self.pk, - 'file_id': self.large_image_file.pk, - } + "identifier_type": "id", + "identifier": self.pk, + "file_id": self.large_image_file.pk, + }, ) - elif self.content_type.name == 'press' and self.object.default_carousel_image: + elif self.content_type.name == "press" and self.object.default_carousel_image: path = self.object.default_carousel_image.url - elif self.content_type.name == 'journal': + elif self.content_type.name == "journal": if self.object.default_large_image: path = self.object.default_large_image.url elif self.object.press.default_carousel_image: @@ -148,17 +169,17 @@ def best_image_url(self): if path: return self.object.site_url(path=path) else: - return '' + return "" def __str__(self): if self.posted_by: - return '{0} posted by {1} on {2}'.format( + return "{0} posted by {1} on {2}".format( self.title, self.posted_by.full_name(), self.posted, ) else: - return '{0} posted on {1}'.format( + return "{0} posted on {1}".format( mark_safe(self.title), self.posted, ) diff --git a/src/comms/urls.py b/src/comms/urls.py index a486503886..702002993e 100755 --- a/src/comms/urls.py +++ b/src/comms/urls.py @@ -3,23 +3,28 @@ from comms import views urlpatterns = [ - re_path(r'^$', views.news_list, name='core_news_list'), - re_path(r'^tag/(?P.*)/$', views.news_list, name='core_news_list_tag'), + re_path(r"^$", views.news_list, name="core_news_list"), + re_path(r"^tag/(?P.*)/$", views.news_list, name="core_news_list_tag"), re_path( - r'^(?Pall)/$', + r"^(?Pall)/$", views.news_list, - name='core_news_list_presswide', + name="core_news_list_presswide", ), re_path( - r'^(?Pall)/tag/(?P.*)/$', + r"^(?Pall)/tag/(?P.*)/$", views.news_list, - name='core_news_list_tag_presswide', + name="core_news_list_tag_presswide", + ), + re_path(r"^manager/$", views.news, name="core_manager_news"), + re_path( + r"^manager/edit/(?P\d+)/$", + views.edit_news, + name="core_manager_edit_news", + ), + re_path(r"^(?P\d+)/$", views.news_item, name="core_news_item"), + re_path( + r"^(?P.+?)/(?P.+)/image/(?P\d+|None)/$", + views.serve_news_file, + name="news_file_download", ), - re_path(r'^manager/$', views.news, name='core_manager_news'), - re_path(r'^manager/edit/(?P\d+)/$', views.edit_news, name='core_manager_edit_news'), - - re_path(r'^(?P\d+)/$', views.news_item, name='core_news_item'), - - re_path(r'^(?P.+?)/(?P.+)/image/(?P\d+|None)/$', - views.serve_news_file, name='news_file_download'), ] diff --git a/src/comms/views.py b/src/comms/views.py index 4be7af9492..ee784d7d2d 100755 --- a/src/comms/views.py +++ b/src/comms/views.py @@ -19,35 +19,46 @@ def news(request): :param request: HttpRequest object :return: HttpResponse object """ - new_items = models.NewsItem.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk).order_by("sequence", "-start_display") + new_items = models.NewsItem.objects.filter( + content_type=request.model_content_type, object_id=request.site_type.pk + ).order_by("sequence", "-start_display") form = forms.NewsItemForm() new_file = None - if 'delete' in request.POST: - news_item_pk = request.POST.get('delete') - item = get_object_or_404(models.NewsItem, - pk=news_item_pk, - content_type=request.model_content_type, - object_id=request.site_type.pk) + if "delete" in request.POST: + news_item_pk = request.POST.get("delete") + item = get_object_or_404( + models.NewsItem, + pk=news_item_pk, + content_type=request.model_content_type, + object_id=request.site_type.pk, + ) item.delete() - return redirect(reverse('core_manager_news')) + return redirect(reverse("core_manager_news")) if request.POST: form = forms.NewsItemForm(request.POST) if request.FILES: - uploaded_file = request.FILES.get('image_file') + uploaded_file = request.FILES.get("image_file") if not files.guess_mime(uploaded_file.name) in files.IMAGE_MIMETYPES: - form.add_error('image_file', 'File must be an image.') + form.add_error("image_file", "File must be an image.") else: - if request.model_content_type.name == 'journal': - new_file = files.save_file_to_journal(request, uploaded_file, 'News Item', 'News Item', public=True) - core_logic.resize_and_crop(new_file.journal_path(request.journal), [750, 324], 'middle') - elif request.model_content_type.name == 'press': - new_file = files.save_file_to_press(request, uploaded_file, 'News Item', 'News Item', public=True) - core_logic.resize_and_crop(new_file.press_path(), [750, 324], 'middle') + if request.model_content_type.name == "journal": + new_file = files.save_file_to_journal( + request, uploaded_file, "News Item", "News Item", public=True + ) + core_logic.resize_and_crop( + new_file.journal_path(request.journal), [750, 324], "middle" + ) + elif request.model_content_type.name == "press": + new_file = files.save_file_to_press( + request, uploaded_file, "News Item", "News Item", public=True + ) + core_logic.resize_and_crop( + new_file.press_path(), [750, 324], "middle" + ) if form.is_valid(): new_item = form.save(commit=False) @@ -58,13 +69,13 @@ def news(request): new_item.large_image_file = new_file new_item.save() - return redirect(reverse('core_manager_news')) + return redirect(reverse("core_manager_news")) - template = 'core/manager/news/index.html' + template = "core/manager/news/index.html" context = { - 'news_items': new_items, - 'action': 'new', - 'form': form, + "news_items": new_items, + "action": "new", + "form": form, } return render(request, template, context) @@ -78,50 +89,63 @@ def edit_news(request, news_pk): :param news_pk: PK of an NewsItem object :return: HttpResponse object """ - new_items = models.NewsItem.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk).order_by('-posted') + new_items = models.NewsItem.objects.filter( + content_type=request.model_content_type, object_id=request.site_type.pk + ).order_by("-posted") news_item = get_object_or_404(models.NewsItem, pk=news_pk) form = forms.NewsItemForm(instance=news_item) new_file = None - if 'delete_image' in request.POST: - delete_image_id = request.POST.get('delete_image') + if "delete_image" in request.POST: + delete_image_id = request.POST.get("delete_image") file = get_object_or_404(core_models.File, pk=delete_image_id) if file.owner == request.user or request.user.is_staff: file.delete() - messages.add_message(request, messages.SUCCESS, 'Image deleted') + messages.add_message(request, messages.SUCCESS, "Image deleted") else: - messages.add_message(request, messages.WARNING, 'Only the owner or staff can delete this image.') - - return redirect(reverse('core_manager_edit_news', kwargs={'news_pk': news_item.pk})) + messages.add_message( + request, + messages.WARNING, + "Only the owner or staff can delete this image.", + ) + + return redirect( + reverse("core_manager_edit_news", kwargs={"news_pk": news_item.pk}) + ) if request.POST: form = forms.NewsItemForm(request.POST, instance=news_item) if request.FILES: - uploaded_file = request.FILES.get('image_file') - - if request.model_content_type.name == 'journal': - new_file = files.save_file_to_journal(request, uploaded_file, 'News Item', 'News Item', public=True) - core_logic.resize_and_crop(new_file.journal_path(request.journal), [750, 324], 'middle') - elif request.model_content_type.name == 'press': - new_file = files.save_file_to_press(request, uploaded_file, 'News Item', 'News Item', public=True) - core_logic.resize_and_crop(new_file.press_path(), [750, 324], 'middle') + uploaded_file = request.FILES.get("image_file") + + if request.model_content_type.name == "journal": + new_file = files.save_file_to_journal( + request, uploaded_file, "News Item", "News Item", public=True + ) + core_logic.resize_and_crop( + new_file.journal_path(request.journal), [750, 324], "middle" + ) + elif request.model_content_type.name == "press": + new_file = files.save_file_to_press( + request, uploaded_file, "News Item", "News Item", public=True + ) + core_logic.resize_and_crop(new_file.press_path(), [750, 324], "middle") if form.is_valid(): item = form.save(commit=False) if new_file: item.large_image_file = new_file item.save() - return redirect(reverse('core_manager_news')) + return redirect(reverse("core_manager_news")) - template = 'core/manager/news/index.html' + template = "core/manager/news/index.html" context = { - 'news_item': news_item, - 'news_items': new_items, - 'action': 'edit', - 'form': form, + "news_item": news_item, + "news_items": new_items, + "action": "edit", + "form": form, } return render(request, template, context) @@ -130,7 +154,7 @@ def edit_news(request, news_pk): @has_request @file_user_required def serve_news_file(request, identifier_type, identifier, file_id): - """ Serves a news file (designed for use in the carousel). + """Serves a news file (designed for use in the carousel). :param request: the request associated with this call :param identifier_type: the identifier type for the article @@ -142,7 +166,7 @@ def serve_news_file(request, identifier_type, identifier, file_id): new_item = models.NewsItem.objects.get( content_type=request.model_content_type, object_id=request.site_type.pk, - pk=identifier + pk=identifier, ) return new_item.serve_news_file() @@ -159,7 +183,7 @@ def news_list(request, tag=None, presswide=False): news_objects = models.NewsItem.active_objects.all() - if not presswide or request.model_content_type.model != 'press': + if not presswide or request.model_content_type.model != "press": news_objects = news_objects.filter( content_type=request.model_content_type, object_id=request.site_type.id, @@ -173,7 +197,7 @@ def news_list(request, tag=None, presswide=False): tag = get_object_or_404(models.Tag, text=unquoted_tag) paginator = Paginator(news_objects, 12) - page = request.GET.get('page', 1) + page = request.GET.get("page", 1) try: news_items = paginator.page(page) @@ -182,19 +206,21 @@ def news_list(request, tag=None, presswide=False): except EmptyPage: news_items = paginator.page(paginator.num_pages) - all_tags = models.Tag.objects.all().annotate( - Count('tags') - ).order_by('-tags__count', 'text') + all_tags = ( + models.Tag.objects.all() + .annotate(Count("tags")) + .order_by("-tags__count", "text") + ) if not request.journal: - template = 'press/core/news/index.html' + template = "press/core/news/index.html" else: - template = 'core/news/index.html' + template = "core/news/index.html" context = { - 'news_items': news_items, - 'tag': tag, - 'all_tags': all_tags, + "news_items": news_items, + "tag": tag, + "all_tags": all_tags, } return render(request, template, context) @@ -207,16 +233,18 @@ def news_item(request, news_pk): :param news_pk: PK of a NewsItem object :return: HttpResponse object """ - item = get_object_or_404(models.NewsItem.objects.prefetch_related('tags'), - pk=news_pk, - content_type=request.model_content_type) + item = get_object_or_404( + models.NewsItem.objects.prefetch_related("tags"), + pk=news_pk, + content_type=request.model_content_type, + ) if request.journal: - template = 'core/news/item.html' + template = "core/news/item.html" else: - template = 'press/core/news/item.html' + template = "press/core/news/item.html" context = { - 'news_item': item, + "news_item": item, } return render(request, template, context) diff --git a/src/copyediting/admin.py b/src/copyediting/admin.py index 53e516cdd0..679ef23c94 100755 --- a/src/copyediting/admin.py +++ b/src/copyediting/admin.py @@ -10,36 +10,64 @@ class CopyeditAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', 'copyeditor', 'editor', 'assigned', - 'decision', 'due', '_journal') - list_filter = ('article__journal', 'assigned', 'due', 'date_decided', - 'copyedit_reopened', 'decision') - search_fields = ('article__title', 'copyeditor__first_name', - 'copyeditor__last_name', 'copyeditor__email', - 'editor__first_name', 'editor__last_name', - 'editor__email', 'editor_note', 'copyeditor_note') - raw_id_fields = ('article', 'copyeditor', 'editor') - filter_horizontal = ('files_for_copyediting', 'copyeditor_files') - - inlines = [ - admin_utils.AuthorReviewInline - ] + list_display = ( + "pk", + "_article", + "copyeditor", + "editor", + "assigned", + "decision", + "due", + "_journal", + ) + list_filter = ( + "article__journal", + "assigned", + "due", + "date_decided", + "copyedit_reopened", + "decision", + ) + search_fields = ( + "article__title", + "copyeditor__first_name", + "copyeditor__last_name", + "copyeditor__email", + "editor__first_name", + "editor__last_name", + "editor__email", + "editor_note", + "copyeditor_note", + ) + raw_id_fields = ("article", "copyeditor", "editor") + filter_horizontal = ("files_for_copyediting", "copyeditor_files") + + inlines = [admin_utils.AuthorReviewInline] class AuthorAdmin(admin.ModelAdmin): - list_display = ('pk', 'author', 'assigned', - 'assignment', '_journal') - list_filter = ('assignment__article__journal', 'assigned', - 'notified', 'date_decided', 'decision') - search_fields = ('assignment__article__title', 'author__first_name', - 'author__last_name', 'author__email', - 'assignment__editor_note', 'assignment__copyeditor_note') - raw_id_fields = ('author', 'assignment') - filter_horizontal = ('files_updated',) - exclude = ('files_updated',) + list_display = ("pk", "author", "assigned", "assignment", "_journal") + list_filter = ( + "assignment__article__journal", + "assigned", + "notified", + "date_decided", + "decision", + ) + search_fields = ( + "assignment__article__title", + "author__first_name", + "author__last_name", + "author__email", + "assignment__editor_note", + "assignment__copyeditor_note", + ) + raw_id_fields = ("author", "assignment") + filter_horizontal = ("files_updated",) + exclude = ("files_updated",) def _journal(self, obj): - return obj.assignment.article.journal.code if obj else '' + return obj.assignment.article.journal.code if obj else "" admin_list = [ diff --git a/src/copyediting/apps.py b/src/copyediting/apps.py index 5794150bd7..aba89209ca 100755 --- a/src/copyediting/apps.py +++ b/src/copyediting/apps.py @@ -8,4 +8,4 @@ class CopyeditingConfig(AppConfig): - name = 'copyediting' + name = "copyediting" diff --git a/src/copyediting/forms.py b/src/copyediting/forms.py index 88785489b4..477c715935 100755 --- a/src/copyediting/forms.py +++ b/src/copyediting/forms.py @@ -14,43 +14,43 @@ class CopyeditAssignmentForm(forms.ModelForm, core_forms.ConfirmableIfErrorsForm): - QUESTION = _('Are you sure you want to create a copyediting assignment?') + QUESTION = _("Are you sure you want to create a copyediting assignment?") class Meta: model = models.CopyeditAssignment fields = ( - 'editor_note', - 'due', - 'files_for_copyediting', - 'copyeditor', + "editor_note", + "due", + "files_for_copyediting", + "copyeditor", ) - attrs = {'required': True} + attrs = {"required": True} widgets = { - 'files_for_copyediting': forms.CheckboxSelectMultiple(attrs=attrs), - 'copyeditor': forms.RadioSelect(attrs=attrs), - 'due': HTMLDateInput, + "files_for_copyediting": forms.CheckboxSelectMultiple(attrs=attrs), + "copyeditor": forms.RadioSelect(attrs=attrs), + "due": HTMLDateInput, } def __init__(self, *args, **kwargs): - copyeditor_pks = kwargs.pop('copyeditor_pks', None) - files = kwargs.pop('files', None) - self.editor = kwargs.pop('editor', None) - self.article = kwargs.pop('article', None) + copyeditor_pks = kwargs.pop("copyeditor_pks", None) + files = kwargs.pop("files", None) + self.editor = kwargs.pop("editor", None) + self.article = kwargs.pop("article", None) super(CopyeditAssignmentForm, self).__init__(*args, **kwargs) if copyeditor_pks: - self.fields['copyeditor'].queryset = Account.objects.filter( + self.fields["copyeditor"].queryset = Account.objects.filter( pk__in=copyeditor_pks, ) if files: - self.fields['files_for_copyediting'].queryset = files + self.fields["files_for_copyediting"].queryset = files def save(self, commit=True): copyedit = super(CopyeditAssignmentForm, self).save(commit=False) - copyedit.copyeditor = self.cleaned_data.get('copyeditor') + copyedit.copyeditor = self.cleaned_data.get("copyeditor") copyedit.editor = self.editor copyedit.article = self.article @@ -59,7 +59,7 @@ def save(self, commit=True): # If saving, an instance exists so we can now add the files. copyedit.files_for_copyediting.add( - *self.cleaned_data.get('files_for_copyediting'), + *self.cleaned_data.get("files_for_copyediting"), ) return copyedit @@ -68,7 +68,7 @@ def check_for_potential_errors(self): # This customizes the confirmable form method potential_errors = [] - copyeditor = self.cleaned_data.get('copyeditor', None) + copyeditor = self.cleaned_data.get("copyeditor", None) message = self.check_for_inactive_account(copyeditor) if message: potential_errors.append(message) @@ -80,49 +80,47 @@ class EditCopyeditAssignment(forms.ModelForm): class Meta: model = models.CopyeditAssignment fields = ( - 'editor_note', - 'due', + "editor_note", + "due", ) widgets = { - 'due': HTMLDateInput, + "due": HTMLDateInput, } class CopyEditForm(forms.ModelForm): class Meta: model = models.CopyeditAssignment - fields = ('copyeditor_note',) + fields = ("copyeditor_note",) class AuthorReviewAssignmentForm(forms.ModelForm, core_forms.ConfirmableForm): - # Note: This form uses ConfirmableForm rather than ConfirmableIfErrorsForm # because there is no first step to the task creation, as with other stages, # so the modal provides a way back if the button is accidentally clicked. # Confirmable form constants - QUESTION = _('Are you sure you want to ask the author to review copyedits?') - CONFIRMABLE_BUTTON_NAME = 'author_review' + QUESTION = _("Are you sure you want to ask the author to review copyedits?") + CONFIRMABLE_BUTTON_NAME = "author_review" class Meta: model = models.AuthorReview # This field is not for user input, # just to make form validation operable - fields = ('author',) + fields = ("author",) def __init__(self, *args, **kwargs): - self.author = kwargs.pop('author', None) - self.assignment = kwargs.pop('assignment', None) - self.notified = kwargs.pop('notified', False) + self.author = kwargs.pop("author", None) + self.assignment = kwargs.pop("assignment", None) + self.notified = kwargs.pop("notified", False) super().__init__(*args, **kwargs) # The way the author field works is a # a temporary workaround arising # from the desire to use core_models.ConfirmableForm # for consistency with other assignments. - self.fields['author'].required = False - + self.fields["author"].required = False def save(self, commit=True): review_assignment = super().save(commit=False) @@ -146,38 +144,41 @@ def check_for_potential_errors(self): class AuthorCopyeditForm(forms.ModelForm, core_forms.ConfirmableForm): - # Confirmable form constants - QUESTION = _('Are you sure you want to complete the copyedit task?') + QUESTION = _("Are you sure you want to complete the copyedit task?") class Meta: model = models.AuthorReview - fields = ('decision', 'author_note') + fields = ("decision", "author_note") def __init__(self, *args, **kwargs): super(AuthorCopyeditForm, self).__init__(*args, **kwargs) - self.fields['decision'].required = True + self.fields["decision"].required = True def check_for_potential_errors(self): # This customizes the confirmable form method potential_errors = [] - if not self.cleaned_data.get('author_note', None): - message = 'The Note to the Editor field is empty.' + if not self.cleaned_data.get("author_note", None): + message = "The Note to the Editor field is empty." potential_errors.append(_(message)) copyedit = self.instance.assignment ce_files = copyedit.copyeditor_files.all() if ce_files: last_upload = max(set(ce_file.date_uploaded for ce_file in ce_files)) - last_editor_action = max(filter(bool, [ - copyedit.copyeditor_completed, - copyedit.copyedit_reopened_complete, - copyedit.assigned - ])) + last_editor_action = max( + filter( + bool, + [ + copyedit.copyeditor_completed, + copyedit.copyedit_reopened_complete, + copyedit.assigned, + ], + ) + ) if last_editor_action > last_upload: - message = 'The copyedited files have not been changed.' + message = "The copyedited files have not been changed." potential_errors.append(_(message)) return potential_errors - diff --git a/src/copyediting/logic.py b/src/copyediting/logic.py index c2eb5bbf83..29302654c8 100755 --- a/src/copyediting/logic.py +++ b/src/copyediting/logic.py @@ -24,10 +24,9 @@ def get_copyeditors(article): copyeditor_assignments = models.CopyeditAssignment.objects.filter( article=article, copyedit_accepted__isnull=True, - ).values('copyeditor__id') + ).values("copyeditor__id") return core_models.AccountRole.objects.filter( - role__slug='copyeditor', - journal=article.journal + role__slug="copyeditor", journal=article.journal ).exclude( user__pk__in=copyeditor_assignments, ) @@ -39,7 +38,7 @@ def get_user_from_post(request): :param post: request.POST object :return: a Account object or None """ - user_id = request.POST.get('copyeditor') + user_id = request.POST.get("copyeditor") if user_id: user = core_models.Account.objects.get(pk=user_id) @@ -60,18 +59,16 @@ def get_copyeditor_notification_context(request, article, copyedit): :param copyedit: CopyeditAssignment Object :return: a template rendered into a string """ - copyedit_requests_url = request.journal.site_url( - reverse("copyedit_requests")) + copyedit_requests_url = request.journal.site_url(reverse("copyedit_requests")) email_context = { - 'article': article, - 'assignment': copyedit, - 'copyedit_requests_url': copyedit_requests_url, + "article": article, + "assignment": copyedit, + "copyedit_requests_url": copyedit_requests_url, } return email_context -def get_copyedit_message(request, article, copyedit, template, - author_review=None): +def get_copyedit_message(request, article, copyedit, template, author_review=None): """ Takes a set of variables and renders a template into a string. :param request: HttpReqest object @@ -84,43 +81,43 @@ def get_copyedit_message(request, article, copyedit, template, :return: """ if author_review: - copyedit_review_url = request.journal.site_url(path=reverse( - 'author_copyedit', args=[article.pk, author_review.pk])) + copyedit_review_url = request.journal.site_url( + path=reverse("author_copyedit", args=[article.pk, author_review.pk]) + ) else: copyedit_review_url = None - copyedit_requests_url = request.journal.site_url(path=reverse( - 'copyedit_requests')) + copyedit_requests_url = request.journal.site_url(path=reverse("copyedit_requests")) email_context = { - 'article': article, - 'assignment': copyedit, - 'author_review': author_review, - 'author_copyedit_url': copyedit_review_url, - 'copyedit_requests_url': copyedit_requests_url, + "article": article, + "assignment": copyedit, + "author_review": author_review, + "author_copyedit_url": copyedit_review_url, + "copyedit_requests_url": copyedit_requests_url, } return render_template.get_message_content(request, email_context, template) + def get_author_copyedit_message_context(request, copyedit, author_review): article = copyedit.article - copyedit_review_url = request.journal.site_url(path=reverse( - 'author_copyedit', args=[article.pk, author_review.pk])) + copyedit_review_url = request.journal.site_url( + path=reverse("author_copyedit", args=[article.pk, author_review.pk]) + ) - copyedit_requests_url = request.journal.site_url(path=reverse( - 'copyedit_requests')) + copyedit_requests_url = request.journal.site_url(path=reverse("copyedit_requests")) email_context = { - 'article': article, - 'assignment': copyedit, - 'author_review': author_review, - 'author_copyedit_url': copyedit_review_url, - 'copyedit_requests_url': copyedit_requests_url, + "article": article, + "assignment": copyedit, + "author_review": author_review, + "author_copyedit_url": copyedit_review_url, + "copyedit_requests_url": copyedit_requests_url, } return email_context - def handle_file_post(request, copyedit): """ Handles uploading of copyediting files, checks a file has been selected and a label has been entered, Assigs the @@ -130,16 +127,18 @@ def handle_file_post(request, copyedit): :return: None or a list of errors """ errors = [] - file = request.FILES.get('file') - file_label = request.POST.get('label') + file = request.FILES.get("file") + file_label = request.POST.get("label") if not file: - errors.append('You must select a file.') + errors.append("You must select a file.") if not file_label: - errors.append('You must add a label for your file.') + errors.append("You must add a label for your file.") if not errors: - new_file = files.save_file_to_article(file, copyedit.article, request.user, label=file_label) + new_file = files.save_file_to_article( + file, copyedit.article, request.user, label=file_label + ) copyedit.copyeditor_files.add(new_file) return None else: @@ -154,29 +153,29 @@ def accept_copyedit(copyedit, article, request): :param request: HttpRequest object :return: None """ - user_message_content = request.POST.get('accept_note') + user_message_content = request.POST.get("accept_note") kwargs = { - 'copyedit_assignment': copyedit, - 'article': article, - 'user_message_content': user_message_content, - 'request': request, - 'skip': True if 'skip' in request.POST else False + "copyedit_assignment": copyedit, + "article": article, + "user_message_content": user_message_content, + "request": request, + "skip": True if "skip" in request.POST else False, } event_logic.Events.raise_event(event_logic.Events.ON_COPYEDIT_ACKNOWLEDGE, **kwargs) copyedit.copyedit_accepted = timezone.now() - if 'skip' not in request.POST: + if "skip" not in request.POST: copyedit.copyedit_acknowledged = True copyedit.save() def reset_copyedit(copyedit, article, request): - user_message_content = request.POST.get('reset_note') - due = request.POST.get('due') + user_message_content = request.POST.get("reset_note") + due = request.POST.get("due") copyedit.copyedit_reopened = timezone.now() copyedit.copyedit_reopened_complete = None @@ -184,27 +183,27 @@ def reset_copyedit(copyedit, article, request): copyedit.save() kwargs = { - 'copyedit_assignment': copyedit, - 'article': article, - 'user_message_content': user_message_content, - 'request': request, - 'skip': True if 'skip' in request.POST else False + "copyedit_assignment": copyedit, + "article": article, + "user_message_content": user_message_content, + "request": request, + "skip": True if "skip" in request.POST else False, } event_logic.Events.raise_event(event_logic.Events.ON_COPYEDIT_REOPEN, **kwargs) def attempt_to_serve_file(request, copyedit): - if request.GET.get('file_id', None): - file_id = request.GET.get('file_id') - _type = request.GET.get('type', None) + if request.GET.get("file_id", None): + file_id = request.GET.get("file_id") + _type = request.GET.get("type", None) try: - if _type == 'for_copyedit': + if _type == "for_copyedit": file = copyedit.files_for_copyediting.get(pk=file_id) else: file = copyedit.copyeditor_files.get(pk=file_id) return files.serve_file(request, file, copyedit.article) except core_models.File.DoesNotExist: - messages.add_message(request, messages.WARNING, 'File does not exist.') + messages.add_message(request, messages.WARNING, "File does not exist.") raise Http404 diff --git a/src/copyediting/migrations/0001_initial.py b/src/copyediting/migrations/0001_initial.py index 503abb30df..605b4b8367 100755 --- a/src/copyediting/migrations/0001_initial.py +++ b/src/copyediting/migrations/0001_initial.py @@ -7,40 +7,97 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='AuthorReview', + name="AuthorReview", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('decision', models.CharField(blank=True, choices=[('accept', 'Accept'), ('corrections', 'Corrections Required')], help_text="Select a decision that reflects your actions. If you've uploaded a new version of the file, select Corrections Required, if you're happy with the copyedit, select Accept.", max_length=20, null=True, verbose_name='Your Decision')), - ('date_decided', models.DateTimeField(blank=True, null=True)), - ('author_note', models.TextField(blank=True, null=True, verbose_name='Note to the Editor')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ( + "decision", + models.CharField( + blank=True, + choices=[ + ("accept", "Accept"), + ("corrections", "Corrections Required"), + ], + help_text="Select a decision that reflects your actions. If you've uploaded a new version of the file, select Corrections Required, if you're happy with the copyedit, select Accept.", + max_length=20, + null=True, + verbose_name="Your Decision", + ), + ), + ("date_decided", models.DateTimeField(blank=True, null=True)), + ( + "author_note", + models.TextField( + blank=True, null=True, verbose_name="Note to the Editor" + ), + ), ], ), migrations.CreateModel( - name='CopyeditAssignment', + name="CopyeditAssignment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('due', models.DateField(default=django.utils.timezone.now)), - ('editor_note', models.TextField(blank=True, null=True, verbose_name='Note for the Copyeditor')), - ('copyeditor_note', models.TextField(blank=True, null=True, verbose_name='Note for the Editor')), - ('decision', models.CharField(blank=True, choices=[('accept', 'Accept'), ('decline', 'Decline')], max_length=20, null=True)), - ('date_decided', models.DateTimeField(blank=True, null=True)), - ('copyeditor_completed', models.DateTimeField(blank=True, null=True)), - ('copyedit_reopened', models.DateTimeField(blank=True, null=True)), - ('copyedit_reopened_complete', models.DateTimeField(blank=True, help_text='The date a reopen was complete', null=True)), - ('copyedit_accepted', models.DateTimeField(blank=True, null=True)), - ('copyedit_acknowledged', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("due", models.DateField(default=django.utils.timezone.now)), + ( + "editor_note", + models.TextField( + blank=True, null=True, verbose_name="Note for the Copyeditor" + ), + ), + ( + "copyeditor_note", + models.TextField( + blank=True, null=True, verbose_name="Note for the Editor" + ), + ), + ( + "decision", + models.CharField( + blank=True, + choices=[("accept", "Accept"), ("decline", "Decline")], + max_length=20, + null=True, + ), + ), + ("date_decided", models.DateTimeField(blank=True, null=True)), + ("copyeditor_completed", models.DateTimeField(blank=True, null=True)), + ("copyedit_reopened", models.DateTimeField(blank=True, null=True)), + ( + "copyedit_reopened_complete", + models.DateTimeField( + blank=True, + help_text="The date a reopen was complete", + null=True, + ), + ), + ("copyedit_accepted", models.DateTimeField(blank=True, null=True)), + ("copyedit_acknowledged", models.BooleanField(default=False)), ], ), ] diff --git a/src/copyediting/migrations/0002_auto_20170711_1203.py b/src/copyediting/migrations/0002_auto_20170711_1203.py index ad1d1dfec3..9bf3b11537 100755 --- a/src/copyediting/migrations/0002_auto_20170711_1203.py +++ b/src/copyediting/migrations/0002_auto_20170711_1203.py @@ -8,59 +8,77 @@ class Migration(migrations.Migration): - initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0001_initial'), - ('submission', '0001_initial'), - ('copyediting', '0001_initial'), + ("core", "0001_initial"), + ("submission", "0001_initial"), + ("copyediting", "0001_initial"), ] operations = [ migrations.AddField( - model_name='copyeditassignment', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="copyeditassignment", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='copyeditassignment', - name='copyeditor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='copyeditor', to=settings.AUTH_USER_MODEL), + model_name="copyeditassignment", + name="copyeditor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="copyeditor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='copyeditassignment', - name='copyeditor_files', - field=models.ManyToManyField(to='core.File'), + model_name="copyeditassignment", + name="copyeditor_files", + field=models.ManyToManyField(to="core.File"), ), migrations.AddField( - model_name='copyeditassignment', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cp_editor', to=settings.AUTH_USER_MODEL), + model_name="copyeditassignment", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cp_editor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='copyeditassignment', - name='files_for_copyediting', - field=models.ManyToManyField(related_name='files_for_copyediting', to='core.File'), + model_name="copyeditassignment", + name="files_for_copyediting", + field=models.ManyToManyField( + related_name="files_for_copyediting", to="core.File" + ), ), migrations.AddField( - model_name='authorreview', - name='assignment', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='copyediting.CopyeditAssignment'), + model_name="authorreview", + name="assignment", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="copyediting.CopyeditAssignment", + ), ), migrations.AddField( - model_name='authorreview', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="authorreview", + name="author", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), ), migrations.AddField( - model_name='authorreview', - name='files_updated', - field=models.ManyToManyField(to='core.File', verbose_name='files_updated'), + model_name="authorreview", + name="files_updated", + field=models.ManyToManyField(to="core.File", verbose_name="files_updated"), ), migrations.AlterUniqueTogether( - name='copyeditassignment', - unique_together=set([('article', 'copyeditor')]), + name="copyeditassignment", + unique_together=set([("article", "copyeditor")]), ), ] diff --git a/src/copyediting/migrations/0003_auto_20180122_2211.py b/src/copyediting/migrations/0003_auto_20180122_2211.py index 64947be65b..66f3df9f38 100644 --- a/src/copyediting/migrations/0003_auto_20180122_2211.py +++ b/src/copyediting/migrations/0003_auto_20180122_2211.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0002_auto_20170711_1203'), + ("copyediting", "0002_auto_20170711_1203"), ] operations = [ migrations.AlterUniqueTogether( - name='copyeditassignment', + name="copyeditassignment", unique_together=set([]), ), ] diff --git a/src/copyediting/migrations/0004_auto_20180412_1544.py b/src/copyediting/migrations/0004_auto_20180412_1544.py index a1f20d2a06..358ced78f5 100644 --- a/src/copyediting/migrations/0004_auto_20180412_1544.py +++ b/src/copyediting/migrations/0004_auto_20180412_1544.py @@ -6,15 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0003_auto_20180122_2211'), + ("copyediting", "0003_auto_20180122_2211"), ] operations = [ migrations.AlterField( - model_name='copyeditassignment', - name='decision', - field=models.CharField(blank=True, choices=[('accept', 'Accept'), ('decline', 'Decline'), ('cancelled', 'Cancelled')], max_length=20, null=True), + model_name="copyeditassignment", + name="decision", + field=models.CharField( + blank=True, + choices=[ + ("accept", "Accept"), + ("decline", "Decline"), + ("cancelled", "Cancelled"), + ], + max_length=20, + null=True, + ), ), ] diff --git a/src/copyediting/migrations/0005_auto_20190523_1758.py b/src/copyediting/migrations/0005_auto_20190523_1758.py index 564d0334a5..3b461be49f 100644 --- a/src/copyediting/migrations/0005_auto_20190523_1758.py +++ b/src/copyediting/migrations/0005_auto_20190523_1758.py @@ -7,21 +7,21 @@ def replace_author_review_setting(apps, schema_editor): try: - Setting = apps.get_model('core', 'Setting') - SettingValue = apps.get_model('core', 'SettingValue') - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') - Journal = apps.get_model('journal', 'Journal') + Setting = apps.get_model("core", "Setting") + SettingValue = apps.get_model("core", "SettingValue") + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") + Journal = apps.get_model("journal", "Journal") setting = Setting.objects.filter( - name='copyeditor_notify_author', - group__name='email', + name="copyeditor_notify_author", + group__name="email", ) journals = Journal.objects.all() values_to_replace = [ - '{{journal.site_url}}{{ url }}', - '{{request.journal.site_url}}{{ url }}' + "{{journal.site_url}}{{ url }}", + "{{request.journal.site_url}}{{ url }}", ] for journal in journals: @@ -45,13 +45,13 @@ def replace_author_review_setting(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0002_auto_20170711_1203'), - ('copyediting', '0004_auto_20180412_1544'), + ("core", "0002_auto_20170711_1203"), + ("copyediting", "0004_auto_20180412_1544"), ] operations = [ - migrations.RunPython(replace_author_review_setting, - reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_author_review_setting, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/copyediting/migrations/0006_auto_20200117_1240.py b/src/copyediting/migrations/0006_auto_20200117_1240.py index f4c91ce8db..d107acd37f 100644 --- a/src/copyediting/migrations/0006_auto_20200117_1240.py +++ b/src/copyediting/migrations/0006_auto_20200117_1240.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0005_auto_20190523_1758'), + ("copyediting", "0005_auto_20190523_1758"), ] operations = [ migrations.AlterField( - model_name='copyeditassignment', - name='copyeditor_files', - field=models.ManyToManyField(blank=True, to='core.File'), + model_name="copyeditassignment", + name="copyeditor_files", + field=models.ManyToManyField(blank=True, to="core.File"), ), ] diff --git a/src/copyediting/migrations/0007_auto_20210120_1145.py b/src/copyediting/migrations/0007_auto_20210120_1145.py index a16160df7e..f8a6c71022 100644 --- a/src/copyediting/migrations/0007_auto_20210120_1145.py +++ b/src/copyediting/migrations/0007_auto_20210120_1145.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0006_auto_20200117_1240'), + ("copyediting", "0006_auto_20200117_1240"), ] operations = [ migrations.AlterField( - model_name='authorreview', - name='files_updated', - field=models.ManyToManyField(blank=True, to='core.File', verbose_name='files_updated'), + model_name="authorreview", + name="files_updated", + field=models.ManyToManyField( + blank=True, to="core.File", verbose_name="files_updated" + ), ), ] diff --git a/src/copyediting/migrations/0008_fix_copyeditor_notify_author.py b/src/copyediting/migrations/0008_fix_copyeditor_notify_author.py index 2796f4bad8..d2bbffa60b 100644 --- a/src/copyediting/migrations/0008_fix_copyeditor_notify_author.py +++ b/src/copyediting/migrations/0008_fix_copyeditor_notify_author.py @@ -9,28 +9,28 @@ def replace_author_review_setting(apps, schema_editor): values_to_replace = [ - '{{journal.site_url}}{{ url }}', - '{{request.journal.site_url}}{{ url }}', - '{{ copyedit_review_url }}' + "{{journal.site_url}}{{ url }}", + "{{request.journal.site_url}}{{ url }}", + "{{ copyedit_review_url }}", ] migration_utils.update_translated_settings( apps, - setting_name='copyeditor_notify_author', - group_name='email', + setting_name="copyeditor_notify_author", + group_name="email", values_to_replace=values_to_replace, replacement_value="{{ author_copyedit_url }}", ) class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0007_auto_20210120_1145'), - ('core', '0053_auto_20210629_0641'), + ("copyediting", "0007_auto_20210120_1145"), + ("core", "0053_auto_20210629_0641"), ] operations = [ - migrations.RunPython(replace_author_review_setting, - reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_author_review_setting, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/copyediting/migrations/0009_copyeditassignment_order.py b/src/copyediting/migrations/0009_copyeditassignment_order.py index d89a823cf0..c326e04778 100644 --- a/src/copyediting/migrations/0009_copyeditassignment_order.py +++ b/src/copyediting/migrations/0009_copyeditassignment_order.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('copyediting', '0008_fix_copyeditor_notify_author'), + ("copyediting", "0008_fix_copyeditor_notify_author"), ] operations = [ migrations.AlterModelOptions( - name='copyeditassignment', - options={'ordering': ('assigned',)}, + name="copyeditassignment", + options={"ordering": ("assigned",)}, ), ] diff --git a/src/copyediting/migrations/0010_alter_copyeditassignment_copyeditor_files.py b/src/copyediting/migrations/0010_alter_copyeditassignment_copyeditor_files.py index aa895a39e7..40a05621a5 100644 --- a/src/copyediting/migrations/0010_alter_copyeditassignment_copyeditor_files.py +++ b/src/copyediting/migrations/0010_alter_copyeditassignment_copyeditor_files.py @@ -4,16 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0099_alter_accountrole_options'), - ('copyediting', '0009_copyeditassignment_order'), + ("core", "0099_alter_accountrole_options"), + ("copyediting", "0009_copyeditassignment_order"), ] operations = [ migrations.AlterField( - model_name='copyeditassignment', - name='copyeditor_files', - field=models.ManyToManyField(blank=True, related_name='copyeditor_files', to='core.file'), + model_name="copyeditassignment", + name="copyeditor_files", + field=models.ManyToManyField( + blank=True, related_name="copyeditor_files", to="core.file" + ), ), ] diff --git a/src/copyediting/models.py b/src/copyediting/models.py index 6333bbe0af..d9bc22c7a8 100644 --- a/src/copyediting/models.py +++ b/src/copyediting/models.py @@ -12,54 +12,72 @@ def copyeditor_decisions(): return ( - ('accept', 'Accept'), - ('decline', 'Decline'), - ('cancelled', 'Cancelled'), + ("accept", "Accept"), + ("decline", "Decline"), + ("cancelled", "Cancelled"), ) def author_decisions(): return ( - ('accept', 'Accept'), - ('corrections', 'Corrections Required'), + ("accept", "Accept"), + ("corrections", "Corrections Required"), ) class ActiveCopyeditAssignmentManager(models.Manager): def get_queryset(self): - return super(ActiveCopyeditAssignmentManager, self).get_queryset().exclude( - article__stage=submission_models.STAGE_ARCHIVED, + return ( + super(ActiveCopyeditAssignmentManager, self) + .get_queryset() + .exclude( + article__stage=submission_models.STAGE_ARCHIVED, + ) ) class CopyeditAssignment(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) - copyeditor = models.ForeignKey('core.Account', related_name='copyeditor', null=True, on_delete=models.SET_NULL) - editor = models.ForeignKey('core.Account', related_name='cp_editor', null=True, on_delete=models.SET_NULL) + copyeditor = models.ForeignKey( + "core.Account", related_name="copyeditor", null=True, on_delete=models.SET_NULL + ) + editor = models.ForeignKey( + "core.Account", related_name="cp_editor", null=True, on_delete=models.SET_NULL + ) assigned = models.DateTimeField(default=timezone.now) notified = models.BooleanField(default=False) due = models.DateField(default=timezone.now) - editor_note = models.TextField(blank=True, null=True, verbose_name='Note for the Copyeditor') - copyeditor_note = models.TextField(blank=True, null=True, verbose_name='Note for the Editor') + editor_note = models.TextField( + blank=True, null=True, verbose_name="Note for the Copyeditor" + ) + copyeditor_note = models.TextField( + blank=True, null=True, verbose_name="Note for the Editor" + ) - decision = models.CharField(max_length=20, choices=copyeditor_decisions(), null=True, blank=True) + decision = models.CharField( + max_length=20, choices=copyeditor_decisions(), null=True, blank=True + ) date_decided = models.DateTimeField(null=True, blank=True) - files_for_copyediting = models.ManyToManyField('core.File', related_name='files_for_copyediting') + files_for_copyediting = models.ManyToManyField( + "core.File", related_name="files_for_copyediting" + ) copyeditor_files = models.ManyToManyField( - 'core.File', + "core.File", blank=True, - related_name='copyeditor_files', + related_name="copyeditor_files", ) copyeditor_completed = models.DateTimeField(blank=True, null=True) copyedit_reopened = models.DateTimeField(blank=True, null=True) - copyedit_reopened_complete = models.DateTimeField(blank=True, null=True, help_text="The date a reopen was complete") + copyedit_reopened_complete = models.DateTimeField( + blank=True, null=True, help_text="The date a reopen was complete" + ) copyedit_accepted = models.DateTimeField(blank=True, null=True) copyedit_acknowledged = models.BooleanField(default=False) @@ -68,23 +86,35 @@ class CopyeditAssignment(models.Model): active_objects = ActiveCopyeditAssignmentManager() class Meta: - ordering = ('assigned',) + ordering = ("assigned",) def __str__(self): - return "Assignment of {0} to {1}".format(self.copyeditor.full_name(), self.article) + return "Assignment of {0} to {1}".format( + self.copyeditor.full_name(), self.article + ) def author_reviews(self): - return AuthorReview.objects.filter(assignment=self).order_by('assigned') + return AuthorReview.objects.filter(assignment=self).order_by("assigned") def active_author_reviews(self): return AuthorReview.objects.filter(assignment=self, date_decided__isnull=True) def actions_available(self): - if self.decision == 'accept' and self.copyeditor_completed and not self.copyedit_reopened and not \ - self.copyedit_accepted and not self.incomplete_author_reviews(): + if ( + self.decision == "accept" + and self.copyeditor_completed + and not self.copyedit_reopened + and not self.copyedit_accepted + and not self.incomplete_author_reviews() + ): return True - elif self.decision == 'accept' and self.copyeditor_completed and self.copyedit_reopened_complete and not \ - self.copyedit_accepted and not self.incomplete_author_reviews(): + elif ( + self.decision == "accept" + and self.copyeditor_completed + and self.copyedit_reopened_complete + and not self.copyedit_accepted + and not self.incomplete_author_reviews() + ): return True else: return False @@ -100,38 +130,87 @@ def incomplete_author_reviews(self): def copyedit_log(self): log = list() - log.append({'date': self.assigned, 'event': 'Copyedit request assigned', 'slug': 'assigned'}) + log.append( + { + "date": self.assigned, + "event": "Copyedit request assigned", + "slug": "assigned", + } + ) for review in self.author_reviews(): - log.append({'date': review.assigned, 'event': 'Author review requested', 'slug': 'author_review'}) + log.append( + { + "date": review.assigned, + "event": "Author review requested", + "slug": "author_review", + } + ) if review.date_decided: - log.append({'date': review.date_decided, 'event': 'Author completed review', - 'slug': 'author_complete'}) + log.append( + { + "date": review.date_decided, + "event": "Author completed review", + "slug": "author_complete", + } + ) if self.copyeditor_completed: - log.append({'date': self.copyeditor_completed, 'event': 'Initial copyedit complete', 'slug': 'initial'}) + log.append( + { + "date": self.copyeditor_completed, + "event": "Initial copyedit complete", + "slug": "initial", + } + ) if self.copyedit_reopened: - log.append({'date': self.copyedit_reopened, 'event': 'Copyedit reopened', 'slug': 'reopened'}) + log.append( + { + "date": self.copyedit_reopened, + "event": "Copyedit reopened", + "slug": "reopened", + } + ) if self.date_decided: - log.append({'date': self.date_decided, 'event': 'Copyeditor request decision: {0}'.format(self.decision), - 'slug': 'decision'}) + log.append( + { + "date": self.date_decided, + "event": "Copyeditor request decision: {0}".format(self.decision), + "slug": "decision", + } + ) if self.copyedit_reopened_complete: - log.append({'date': self.copyedit_reopened_complete, 'event': 'Copyeditor completed additional edits', - 'slug': 'reopened_complete'}) + log.append( + { + "date": self.copyedit_reopened_complete, + "event": "Copyeditor completed additional edits", + "slug": "reopened_complete", + } + ) if self.copyedit_accepted: - log.append({'date': self.copyedit_accepted, 'event': 'Editor accepted the copyedit', 'slug': 'accepted'}) - return sorted(log, key=lambda k: k['date']) + log.append( + { + "date": self.copyedit_accepted, + "event": "Editor accepted the copyedit", + "slug": "accepted", + } + ) + return sorted(log, key=lambda k: k["date"]) class ActiveAuthorReviewManager(models.Manager): def get_queryset(self): - return super(ActiveAuthorReviewManager, self).get_queryset().exclude( - assignment__article__stage=submission_models.STAGE_ARCHIVED, + return ( + super(ActiveAuthorReviewManager, self) + .get_queryset() + .exclude( + assignment__article__stage=submission_models.STAGE_ARCHIVED, + ) ) class AuthorReview(models.Model): author = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) assignment = models.ForeignKey( @@ -142,17 +221,24 @@ class AuthorReview(models.Model): assigned = models.DateTimeField(default=timezone.now) notified = models.BooleanField(default=False) - decision = models.CharField(max_length=20, choices=author_decisions(), null=True, blank=True, - help_text='Select a decision that reflects your actions. If you\'ve uploaded a new ' - 'version of the file, select Corrections Required, if you\'re happy with the ' - 'copyedit, select Accept.', - verbose_name='Your Decision') + decision = models.CharField( + max_length=20, + choices=author_decisions(), + null=True, + blank=True, + help_text="Select a decision that reflects your actions. If you've uploaded a new " + "version of the file, select Corrections Required, if you're happy with the " + "copyedit, select Accept.", + verbose_name="Your Decision", + ) date_decided = models.DateTimeField(null=True, blank=True) - author_note = models.TextField(null=True, blank=True, verbose_name='Note to the Editor') + author_note = models.TextField( + null=True, blank=True, verbose_name="Note to the Editor" + ) files_updated = models.ManyToManyField( - 'core.File', - verbose_name='files_updated', + "core.File", + verbose_name="files_updated", blank=True, ) diff --git a/src/copyediting/tests.py b/src/copyediting/tests.py index 1a73dd5509..b4bfe1ec6d 100644 --- a/src/copyediting/tests.py +++ b/src/copyediting/tests.py @@ -8,27 +8,26 @@ class TestLogic(TestCase): - @classmethod def setUpTestData(cls): cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() cls.editor = helpers.create_user( - 'typesetter@janeway.systems', - ['editor'], + "typesetter@janeway.systems", + ["editor"], journal=cls.journal_one, - atrrs={'is_active': True} + atrrs={"is_active": True}, ) cls.copyeditor = helpers.create_user( - 'copyeditor@janeway.systems', - ['copyeditor'], + "copyeditor@janeway.systems", + ["copyeditor"], journal=cls.journal_one, ) cls.copyeditor.is_active = True cls.copyeditor.save() cls.author = helpers.create_user( - 'author@janeway.systems', - ['author'], + "author@janeway.systems", + ["author"], journal=cls.journal_one, ) cls.author.is_active = True @@ -36,14 +35,14 @@ def setUpTestData(cls): cls.active_article = helpers.create_article( journal=cls.journal_one, ) - cls.active_article.title = 'Active Article' + cls.active_article.title = "Active Article" cls.active_article.save() cls.active_article.authors.add(cls.author) cls.archived_article = helpers.create_article( journal=cls.journal_one, ) cls.archived_article.stage = submission_models.STAGE_ARCHIVED - cls.archived_article.title = 'Archived Article' + cls.archived_article.title = "Archived Article" cls.archived_article.save() cls.archived_article.authors.add(cls.author) @@ -69,26 +68,18 @@ def setUpTestData(cls): def test_archive_stage_hides_task(self): self.client.force_login(self.copyeditor) - response = self.client.get( - reverse('copyedit_requests') - ) + response = self.client.get(reverse("copyedit_requests")) self.assertContains( response, - 'Active Article', - ) - self.assertNotContains( - response, - 'Archived Article' + "Active Article", ) + self.assertNotContains(response, "Archived Article") def test_archived_article_task_404s(self): self.client.force_login(self.copyeditor) response = self.client.get( reverse( - 'do_copyedit', - kwargs={ - 'copyedit_id': self.archived_copyediting_task.pk - } + "do_copyedit", kwargs={"copyedit_id": self.archived_copyediting_task.pk} ) ) self.assertTrue( @@ -100,10 +91,7 @@ def test_active_article_task_200s(self): self.client.force_login(self.copyeditor) response = self.client.get( reverse( - 'do_copyedit', - kwargs={ - 'copyedit_id': self.active_copyediting_task.pk - } + "do_copyedit", kwargs={"copyedit_id": self.active_copyediting_task.pk} ) ) self.assertTrue( @@ -115,11 +103,11 @@ def test_archived_article_review_task_404s(self): self.client.force_login(self.author) response = self.client.get( reverse( - 'author_copyedit', + "author_copyedit", kwargs={ - 'article_id': self.archived_article.pk, - 'author_review_id': self.archived_author_review.pk - } + "article_id": self.archived_article.pk, + "author_review_id": self.archived_author_review.pk, + }, ) ) self.assertTrue( @@ -131,14 +119,14 @@ def test_active_article_review_task_200s(self): self.client.force_login(self.author) response = self.client.get( reverse( - 'author_copyedit', + "author_copyedit", kwargs={ - 'article_id': self.active_article.pk, - 'author_review_id': self.active_author_review.pk - } + "article_id": self.active_article.pk, + "author_review_id": self.active_author_review.pk, + }, ) ) self.assertTrue( response.status_code, 200, - ) \ No newline at end of file + ) diff --git a/src/copyediting/urls.py b/src/copyediting/urls.py index e606581772..79c1b64754 100755 --- a/src/copyediting/urls.py +++ b/src/copyediting/urls.py @@ -10,36 +10,70 @@ urlpatterns = [ - # Editor URLs - re_path(r'^$', views.copyediting, name='copyediting'), - re_path(r'^article/(?P\d+)/$', views.article_copyediting, name='article_copyediting'), - re_path(r'^article/(?P\d+)/assignment/add/$', views.add_copyeditor_assignment, - name='add_copyeditor_assignment'), - re_path(r'^article/(?P\d+)/assignment/(?P\d+)/$', views.notify_copyeditor_assignment, - name='notify_copyeditor_assignment'), - re_path(r'^article/(?P\d+)/assignment/(?P\d+)/edit/$', views.edit_assignment, - name='copyedit_edit_assignment'), - re_path(r'^article/(?P\d+)/assignment/(?P\d+)/review/$', views.editor_review, - name='editor_review'), - re_path(r'^article/(?P\d+)/assignment/(?P\d+)/author_review/(?P\d+)/$', views.request_author_copyedit, - name='request_author_copyedit'), - re_path(r'^article/(?P\d+)/assignment/(?P\d+)/author_review/(?P\d+)/delete/$', views.delete_author_review, - name='delete_author_review'), - + re_path(r"^$", views.copyediting, name="copyediting"), + re_path( + r"^article/(?P\d+)/$", + views.article_copyediting, + name="article_copyediting", + ), + re_path( + r"^article/(?P\d+)/assignment/add/$", + views.add_copyeditor_assignment, + name="add_copyeditor_assignment", + ), + re_path( + r"^article/(?P\d+)/assignment/(?P\d+)/$", + views.notify_copyeditor_assignment, + name="notify_copyeditor_assignment", + ), + re_path( + r"^article/(?P\d+)/assignment/(?P\d+)/edit/$", + views.edit_assignment, + name="copyedit_edit_assignment", + ), + re_path( + r"^article/(?P\d+)/assignment/(?P\d+)/review/$", + views.editor_review, + name="editor_review", + ), + re_path( + r"^article/(?P\d+)/assignment/(?P\d+)/author_review/(?P\d+)/$", + views.request_author_copyedit, + name="request_author_copyedit", + ), + re_path( + r"^article/(?P\d+)/assignment/(?P\d+)/author_review/(?P\d+)/delete/$", + views.delete_author_review, + name="delete_author_review", + ), # Author URLs - re_path(r'^author/article/(?P\d+)/assignment/(?P\d+)/$', views.author_copyedit, - name='author_copyedit'), - re_path(r'^author/article/(?P\d+)/assignment/(?P\d+)/file/(?P\d+)/update/$', - views.author_update_file, name='author_update_file'), - + re_path( + r"^author/article/(?P\d+)/assignment/(?P\d+)/$", + views.author_copyedit, + name="author_copyedit", + ), + re_path( + r"^author/article/(?P\d+)/assignment/(?P\d+)/file/(?P\d+)/update/$", + views.author_update_file, + name="author_update_file", + ), # Copyeditor URLs - re_path(r'^requests/$', views.copyedit_requests, name='copyedit_requests'), - re_path(r'^requests/(?P\d+)/$', views.do_copyedit, name='do_copyedit'), - re_path(r'^requests/(?P\d+)/files/upload/$', views.do_copyedit_add_file, name='do_copyedit_add_file'), - re_path(r'^requests/(?P\d+)/files/(?P\d+)/download/$', views.copyeditor_file, - name='copyeditor_file'), - re_path(r'^requests/(?P\d+)/(?Paccept|decline)/$', views.copyedit_request_decision, - name='copyedit_request_decision'), - + re_path(r"^requests/$", views.copyedit_requests, name="copyedit_requests"), + re_path(r"^requests/(?P\d+)/$", views.do_copyedit, name="do_copyedit"), + re_path( + r"^requests/(?P\d+)/files/upload/$", + views.do_copyedit_add_file, + name="do_copyedit_add_file", + ), + re_path( + r"^requests/(?P\d+)/files/(?P\d+)/download/$", + views.copyeditor_file, + name="copyeditor_file", + ), + re_path( + r"^requests/(?P\d+)/(?Paccept|decline)/$", + views.copyedit_request_decision, + name="copyedit_request_decision", + ), ] diff --git a/src/copyediting/views.py b/src/copyediting/views.py index 3db9ece638..bdc7940243 100755 --- a/src/copyediting/views.py +++ b/src/copyediting/views.py @@ -14,17 +14,20 @@ from copyediting import models, logic, forms from core import ( - files, - forms as core_forms, - models as core_models, - logic as core_logic, + files, + forms as core_forms, + models as core_models, + logic as core_logic, ) from events import logic as event_logic from security.decorators import ( - production_user_or_editor_required, copyeditor_user_required, - copyeditor_for_copyedit_required, article_author_required, - editor_user_required, senior_editor_user_required, - any_editor_user_required + production_user_or_editor_required, + copyeditor_user_required, + copyeditor_for_copyedit_required, + article_author_required, + editor_user_required, + senior_editor_user_required, + any_editor_user_required, ) from submission import models as submission_models @@ -42,18 +45,17 @@ def copyediting(request): stage__in=submission_models.COPYEDITING_STAGES, journal=request.journal, ).prefetch_related( - 'copyeditassignment_set', + "copyeditassignment_set", ) if not request.user.is_editor(request) and request.user.is_section_editor(request): articles_in_copyediting = core_logic.filter_articles_to_editor_assigned( - request, - articles_in_copyediting + request, articles_in_copyediting ) - template = 'copyediting/copyediting.html' + template = "copyediting/copyediting.html" context = { - 'articles_in_copyediting': articles_in_copyediting, + "articles_in_copyediting": articles_in_copyediting, } return render(request, template, context) @@ -77,19 +79,21 @@ def article_copyediting(request, article_id): article=article, ) - message_kwargs = {'request': request, 'article': article} + message_kwargs = {"request": request, "article": article} - if 'delete' in request.POST: - assignment_id = request.POST.get('delete') - assignment = get_object_or_404(models.CopyeditAssignment, - article=article, - pk=assignment_id, - decision__isnull=True) - message_kwargs['copyedit_assignment'] = assignment + if "delete" in request.POST: + assignment_id = request.POST.get("delete") + assignment = get_object_or_404( + models.CopyeditAssignment, + article=article, + pk=assignment_id, + decision__isnull=True, + ) + message_kwargs["copyedit_assignment"] = assignment messages.add_message( request, messages.SUCCESS, - 'Assignment #{0} delete'.format(assignment_id), + "Assignment #{0} delete".format(assignment_id), ) event_logic.Events.raise_event( event_logic.Events.ON_COPYEDIT_DELETED, @@ -97,20 +101,20 @@ def article_copyediting(request, article_id): ) assignment.delete() return redirect( - reverse('article_copyediting', kwargs={'article_id': article.pk}) + reverse("article_copyediting", kwargs={"article_id": article.pk}) ) - if request.POST and 'complete' in request.POST: + if request.POST and "complete" in request.POST: event_logic.Events.raise_event( event_logic.Events.ON_COPYEDIT_COMPLETE, task_object=article, **message_kwargs, ) workflow_kwargs = { - 'handshake_url': 'copyediting', - 'request': request, - 'article': article, - 'switch_stage': True, + "handshake_url": "copyediting", + "request": request, + "article": article, + "switch_stage": True, } return event_logic.Events.raise_event( event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, @@ -118,11 +122,13 @@ def article_copyediting(request, article_id): **workflow_kwargs, ) - template = 'copyediting/article_copyediting.html' + template = "copyediting/article_copyediting.html" context = { - 'article': article, - 'copyeditor_assignments': copyeditor_assignments, - 'in_copyediting': True if article.stage in submission_models.COPYEDITING_STAGES else False, + "article": article, + "copyeditor_assignments": copyeditor_assignments, + "in_copyediting": True + if article.stage in submission_models.COPYEDITING_STAGES + else False, } return render(request, template, context) @@ -166,19 +172,19 @@ def add_copyeditor_assignment(request, article_id): return redirect( reverse( - 'notify_copyeditor_assignment', + "notify_copyeditor_assignment", kwargs={ - 'article_id': article.pk, - 'copyedit_id': copyedit.pk, - } + "article_id": article.pk, + "copyedit_id": copyedit.pk, + }, ) ) - template = 'copyediting/add_copyeditor_assignment.html' + template = "copyediting/add_copyeditor_assignment.html" context = { - 'article': article, - 'copyeditors': copyeditors, - 'form': form, + "article": article, + "copyeditors": copyeditors, + "form": form, } return render(request, template, context) @@ -200,42 +206,47 @@ def notify_copyeditor_assignment(request, article_id, copyedit_id): ) copyedit = get_object_or_404(models.CopyeditAssignment, pk=copyedit_id) email_context = logic.get_copyeditor_notification_context( - request, article, copyedit, + request, + article, + copyedit, ) form = core_forms.SettingEmailForm( - setting_name="copyeditor_assignment_notification", - email_context=email_context, - request=request, + setting_name="copyeditor_assignment_notification", + email_context=email_context, + request=request, ) if request.POST: form = core_forms.SettingEmailForm( - request.POST, request.FILES, - setting_name="copyeditor_assignment_notification", - email_context=email_context, - request=request, + request.POST, + request.FILES, + setting_name="copyeditor_assignment_notification", + email_context=email_context, + request=request, ) - skip = 'skip' in request.POST + skip = "skip" in request.POST if form.is_valid() or skip: - kwargs = { - 'article': article, - 'copyedit_assignment': copyedit, - 'request': request, - 'skip': skip, - 'email_data': form.as_dataclass() + "article": article, + "copyedit_assignment": copyedit, + "request": request, + "skip": skip, + "email_data": form.as_dataclass(), } event_logic.Events.raise_event( - event_logic.Events.ON_COPYEDIT_ASSIGNMENT, **kwargs) - messages.add_message(request, messages.INFO, 'Copyedit requested.') - return redirect(reverse('article_copyediting', kwargs={'article_id': article.pk})) + event_logic.Events.ON_COPYEDIT_ASSIGNMENT, **kwargs + ) + messages.add_message(request, messages.INFO, "Copyedit requested.") + return redirect( + reverse("article_copyediting", kwargs={"article_id": article.pk}) + ) - template = 'copyediting/notify_copyeditor_assignment.html' + template = "copyediting/notify_copyeditor_assignment.html" context = { - 'article': article, - 'copyedit': copyedit, - 'form': form, + "article": article, + "copyedit": copyedit, + "form": form, } return render(request, template, context) @@ -259,14 +270,12 @@ def edit_assignment(request, article_id, copyedit_id): if copyedit.decision: messages.add_message( - request, - messages.WARNING, - 'This task is underway so cannot be edited.' + request, messages.WARNING, "This task is underway so cannot be edited." ) return redirect( reverse( - 'article_copyediting', - kwargs={'article_id': article.pk}, + "article_copyediting", + kwargs={"article_id": article.pk}, ) ) @@ -282,29 +291,31 @@ def edit_assignment(request, article_id, copyedit_id): if form.is_valid(): form.save() - kwargs = {'copyedit_assignment': copyedit, 'request': request, - 'skip': True if 'skip' in request.POST else False} + kwargs = { + "copyedit_assignment": copyedit, + "request": request, + "skip": True if "skip" in request.POST else False, + } event_logic.Events.raise_event( - event_logic.Events.ON_COPYEDIT_UPDATED, - **kwargs + event_logic.Events.ON_COPYEDIT_UPDATED, **kwargs ) messages.add_message( request, messages.SUCCESS, - 'Copyedit assignment updated.', + "Copyedit assignment updated.", ) return redirect( reverse( - 'article_copyediting', - kwargs={'article_id': article.pk}, + "article_copyediting", + kwargs={"article_id": article.pk}, ) ) - template = 'copyediting/edit_assignment.html' + template = "copyediting/edit_assignment.html" context = { - 'article': article, - 'copyedit': copyedit, - 'form': form, + "article": article, + "copyedit": copyedit, + "form": form, } return render(request, template, context) @@ -318,17 +329,17 @@ def copyedit_requests(request): :return: HttpResponse object """ new_requests = models.CopyeditAssignment.active_objects.filter( - copyeditor=request.user, - decision__isnull=True, - article__journal=request.journal + copyeditor=request.user, decision__isnull=True, article__journal=request.journal ) active_requests = models.CopyeditAssignment.active_objects.filter( - (Q(copyeditor=request.user) & - Q(decision='accept') & - Q(copyedit_reopened__isnull=True) & - Q(copyeditor_completed__isnull=True)), - article__journal=request.journal + ( + Q(copyeditor=request.user) + & Q(decision="accept") + & Q(copyedit_reopened__isnull=True) + & Q(copyeditor_completed__isnull=True) + ), + article__journal=request.journal, ) reopened_requests = models.CopyeditAssignment.active_objects.filter( @@ -341,20 +352,17 @@ def copyedit_requests(request): ) completed_requests = models.CopyeditAssignment.active_objects.filter( - Q(copyeditor=request.user, - decision='accept', - copyedit_accepted__isnull=False) | - Q(copyeditor=request.user, - copyeditor_completed__isnull=False), - article__journal=request.journal + Q(copyeditor=request.user, decision="accept", copyedit_accepted__isnull=False) + | Q(copyeditor=request.user, copyeditor_completed__isnull=False), + article__journal=request.journal, ) - template = 'copyediting/copyedit_requests.html' + template = "copyediting/copyedit_requests.html" context = { - 'new_requests': new_requests, - 'active_requests': active_requests, - 'reopened_requests': reopened_requests, - 'completed_requests': completed_requests, + "new_requests": new_requests, + "active_requests": active_requests, + "reopened_requests": reopened_requests, + "completed_requests": completed_requests, } return render(request, template, context) @@ -371,26 +379,36 @@ def copyedit_request_decision(request, copyedit_id, decision): """ copyedit = get_object_or_404(models.CopyeditAssignment, pk=copyedit_id) - if decision == 'accept': - copyedit.decision = 'accept' + if decision == "accept": + copyedit.decision = "accept" copyedit.date_decided = timezone.now() - messages.add_message(request, messages.INFO, 'Copyediting request {0} accepted'.format(copyedit.pk)) - elif decision == 'decline': - copyedit.decision = 'decline' + messages.add_message( + request, + messages.INFO, + "Copyediting request {0} accepted".format(copyedit.pk), + ) + elif decision == "decline": + copyedit.decision = "decline" copyedit.date_decided = timezone.now() - messages.add_message(request, messages.INFO, 'Copyediting request {0} declined'.format(copyedit.pk)) + messages.add_message( + request, + messages.INFO, + "Copyediting request {0} declined".format(copyedit.pk), + ) else: - messages.add_message(request, messages.WARNING, 'Decision must be "accept" or "decline".') + messages.add_message( + request, messages.WARNING, 'Decision must be "accept" or "decline".' + ) kwargs = { - 'copyedit_assignment': copyedit, - 'decision': decision, - 'request': request, + "copyedit_assignment": copyedit, + "decision": decision, + "request": request, } event_logic.Events.raise_event(event_logic.Events.ON_COPYEDITOR_DECISION, **kwargs) copyedit.save() - return redirect(reverse('copyedit_requests')) + return redirect(reverse("copyedit_requests")) @copyeditor_for_copyedit_required @@ -407,7 +425,7 @@ def do_copyedit(request, copyedit_id): Q(copyeditor_completed__isnull=True) | Q(copyedit_reopened__isnull=False), copyedit_reopened_complete__isnull=True, pk=copyedit_id, - decision='accept', + decision="accept", ) form = forms.CopyEditForm(instance=copyedit) @@ -415,7 +433,10 @@ def do_copyedit(request, copyedit_id): form = forms.CopyEditForm(request.POST, instance=copyedit) if not copyedit.copyeditor_files.all(): - form.add_error(None, 'You must upload a file before you can complete your copyediting task.') + form.add_error( + None, + "You must upload a file before you can complete your copyediting task.", + ) if form.is_valid(): copyedit = form.save() @@ -425,21 +446,25 @@ def do_copyedit(request, copyedit_id): else: copyedit.copyeditor_completed = timezone.now() copyedit.save() - messages.add_message(request, messages.SUCCESS, 'Initial copyedit assignment completed.') + messages.add_message( + request, messages.SUCCESS, "Initial copyedit assignment completed." + ) kwargs = { - 'article': copyedit.article, - 'copyedit_assignment': copyedit, - 'request': request, + "article": copyedit.article, + "copyedit_assignment": copyedit, + "request": request, } - event_logic.Events.raise_event(event_logic.Events.ON_COPYEDIT_ASSIGNMENT_COMPLETE, **kwargs) - return redirect(reverse('copyedit_requests')) + event_logic.Events.raise_event( + event_logic.Events.ON_COPYEDIT_ASSIGNMENT_COMPLETE, **kwargs + ) + return redirect(reverse("copyedit_requests")) - template = 'copyediting/do_copyedit.html' + template = "copyediting/do_copyedit.html" context = { - 'copyedit': copyedit, - 'form': form, + "copyedit": copyedit, + "form": form, } return render(request, template, context) @@ -453,22 +478,24 @@ def do_copyedit_add_file(request, copyedit_id): :param copyedit_id: a CopyeditAssignment PK :return: HttpResponse object """ - copyedit = get_object_or_404(models.CopyeditAssignment, - Q(copyeditor_completed__isnull=True) | Q(copyedit_reopened__isnull=False), - copyedit_reopened_complete__isnull=True, - pk=copyedit_id, - decision='accept') + copyedit = get_object_or_404( + models.CopyeditAssignment, + Q(copyeditor_completed__isnull=True) | Q(copyedit_reopened__isnull=False), + copyedit_reopened_complete__isnull=True, + pk=copyedit_id, + decision="accept", + ) errors = None if request.POST: errors = logic.handle_file_post(request, copyedit) if not errors: - return redirect(reverse('do_copyedit', kwargs={'copyedit_id': copyedit.id})) + return redirect(reverse("do_copyedit", kwargs={"copyedit_id": copyedit.id})) - template = 'copyediting/upload_file.html' + template = "copyediting/upload_file.html" context = { - 'copyedit': copyedit, - 'errors': errors, + "copyedit": copyedit, + "errors": errors, } return render(request, template, context) @@ -476,7 +503,7 @@ def do_copyedit_add_file(request, copyedit_id): @copyeditor_for_copyedit_required def copyeditor_file(request, copyedit_id, file_id): - """ Serves an article file. + """Serves an article file. :param request: the request associated with this call :param copyedit_id: the CopyeditAssignment id. :param file_id: the file ID to serve @@ -486,7 +513,8 @@ def copyeditor_file(request, copyedit_id, file_id): Q(copyeditor_completed__isnull=True) | Q(copyedit_reopened__isnull=False), copyedit_reopened_complete__isnull=True, pk=copyedit_id, - decision='accept') + decision="accept", + ) article_object = copyedit_assignment.article file_object = core_models.File.objects.get(pk=file_id) @@ -519,12 +547,17 @@ def editor_review(request, article_id, copyedit_id): ) if request.POST: - if 'accept_note' in request.POST: + if "accept_note" in request.POST: logic.accept_copyedit(copyedit, article, request) - return redirect(reverse('article_copyediting', kwargs={'article_id': article.id})) + return redirect( + reverse("article_copyediting", kwargs={"article_id": article.id}) + ) - elif 'author_review' in request.POST or author_review_form.CONFIRMED_BUTTON_NAME in request.POST: + elif ( + "author_review" in request.POST + or author_review_form.CONFIRMED_BUTTON_NAME in request.POST + ): author_review_form = forms.AuthorReviewAssignmentForm( request.POST, author=article.correspondence_author, @@ -536,37 +569,42 @@ def editor_review(request, article_id, copyedit_id): return redirect( reverse( - 'request_author_copyedit', + "request_author_copyedit", kwargs={ - 'article_id': article.pk, - 'copyedit_id': copyedit.pk, - 'author_review_id': author_review.pk, - } + "article_id": article.pk, + "copyedit_id": copyedit.pk, + "author_review_id": author_review.pk, + }, ) ) - elif 'reset_note' in request.POST: + elif "reset_note" in request.POST: logic.reset_copyedit(copyedit, article, request) - return redirect(reverse('article_copyediting', kwargs={'article_id': article.id})) + return redirect( + reverse("article_copyediting", kwargs={"article_id": article.id}) + ) - if request.GET.get('file_id'): + if request.GET.get("file_id"): return logic.attempt_to_serve_file(request, copyedit) - template = 'copyediting/editor_review.html' + template = "copyediting/editor_review.html" context = { - 'article': article, - 'copyedit': copyedit, - 'accept_message': logic.get_copyedit_message(request, article, copyedit, 'copyeditor_ack'), - 'reopen_message': logic.get_copyedit_message(request, article, copyedit, 'copyeditor_reopen_task'), - 'author_review_form': author_review_form, + "article": article, + "copyedit": copyedit, + "accept_message": logic.get_copyedit_message( + request, article, copyedit, "copyeditor_ack" + ), + "reopen_message": logic.get_copyedit_message( + request, article, copyedit, "copyeditor_reopen_task" + ), + "author_review_form": author_review_form, } return render(request, template, context) @editor_user_required -def request_author_copyedit(request, article_id, copyedit_id, - author_review_id): +def request_author_copyedit(request, article_id, copyedit_id, author_review_id): """ Allows an editor to request an Author undertake a review. :param request: @@ -598,53 +636,55 @@ def request_author_copyedit(request, article_id, copyedit_id, ) form = core_forms.SettingEmailForm( - setting_name='copyeditor_notify_author', + setting_name="copyeditor_notify_author", email_context=email_context, request=request, ) if request.POST: - skip = 'skip' in request.POST + skip = "skip" in request.POST form = core_forms.SettingEmailForm( - request.POST, request.FILES, - setting_name='copyeditor_notify_author', + request.POST, + request.FILES, + setting_name="copyeditor_notify_author", email_context=email_context, request=request, ) if form.is_valid() or skip: kwargs = { - 'copyedit_assignment': copyedit, - 'article': article, - 'author_review': author_review, - 'email_data': form.as_dataclass(), - 'request': request, - 'skip': skip, + "copyedit_assignment": copyedit, + "article": article, + "author_review": author_review, + "email_data": form.as_dataclass(), + "request": request, + "skip": skip, } event_logic.Events.raise_event( - event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW, **kwargs) + event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW, **kwargs + ) messages.add_message( request, messages.SUCCESS, - 'Author review requested.', + "Author review requested.", ) return redirect( reverse( - 'editor_review', + "editor_review", kwargs={ - 'article_id': article.pk, - 'copyedit_id': copyedit.pk, - } + "article_id": article.pk, + "copyedit_id": copyedit.pk, + }, ) ) - template = 'copyediting/request_author_copyedit.html' + template = "copyediting/request_author_copyedit.html" context = { - 'article': article, - 'copyedit': copyedit, - 'author_review': author_review, - 'form':form, + "article": article, + "copyedit": copyedit, + "author_review": author_review, + "form": form, } return render(request, template, context) @@ -657,7 +697,7 @@ def delete_author_review(request, article_id, copyedit_id, author_review_id): pk=author_review_id, assignment__article__journal=request.journal, ) - article = author_review.assignment.article, + article = (author_review.assignment.article,) copyedit = author_review.assignment email_context = logic.get_author_copyedit_message_context( @@ -667,28 +707,29 @@ def delete_author_review(request, article_id, copyedit_id, author_review_id): ) form = core_forms.SettingEmailForm( - setting_name='author_copyedit_deleted', + setting_name="author_copyedit_deleted", email_context=email_context, request=request, ) if request.POST: - skip = 'skip' in request.POST + skip = "skip" in request.POST form = core_forms.SettingEmailForm( - request.POST, request.FILES, - setting_name='author_copyedit_deleted', + request.POST, + request.FILES, + setting_name="author_copyedit_deleted", email_context=email_context, request=request, ) if form.is_valid() or skip: author_review.delete() kwargs = { - 'copyedit_assignment': copyedit, - 'article': article, - 'author_review': author_review, - 'email_data': form.as_dataclass(), - 'request': request, - 'skip': skip, + "copyedit_assignment": copyedit, + "article": article, + "author_review": author_review, + "email_data": form.as_dataclass(), + "request": request, + "skip": skip, } event_logic.Events.raise_event( event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_DELETED, @@ -697,27 +738,27 @@ def delete_author_review(request, article_id, copyedit_id, author_review_id): messages.add_message( request, messages.SUCCESS, - 'Author review for {} has been deleted.'.format( + "Author review for {} has been deleted.".format( author_review.assignment.article.title, - ) + ), ) return redirect( reverse( - 'editor_review', + "editor_review", kwargs={ - 'article_id': article_id, - 'copyedit_id': copyedit_id, - } + "article_id": article_id, + "copyedit_id": copyedit_id, + }, ) ) - template = 'admin/copyediting/delete_author_review.html' + template = "admin/copyediting/delete_author_review.html" context = { - 'author_review': author_review, - 'article': author_review.assignment.article, - 'copyedit': author_review.assignment, - 'form': form, + "author_review": author_review, + "article": author_review.assignment.article, + "copyedit": author_review.assignment, + "form": form, } return render( request, @@ -752,19 +793,25 @@ def author_copyedit(request, article_id, author_review_id): author_review.date_decided = timezone.now() author_review.save() - kwargs = {'author_review': author_review, 'copyedit': copyedit, 'request': request} - event_logic.Events.raise_event(event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE, **kwargs) + kwargs = { + "author_review": author_review, + "copyedit": copyedit, + "request": request, + } + event_logic.Events.raise_event( + event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE, **kwargs + ) - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) - if request.GET.get('file_id'): + if request.GET.get("file_id"): return logic.attempt_to_serve_file(request, copyedit) - template = 'copyediting/author_review.html' + template = "copyediting/author_review.html" context = { - 'author_review': author_review, - 'copyedit': copyedit, - 'form': form, + "author_review": author_review, + "copyedit": copyedit, + "form": form, } return render(request, template, context) @@ -780,10 +827,12 @@ def author_update_file(request, article_id, author_review_id, file_id): :param file_id: File pk :return: contextualised django template """ - author_review = get_object_or_404(models.AuthorReview, - pk=author_review_id, - assignment__article__id=article_id, - date_decided__isnull=True) + author_review = get_object_or_404( + models.AuthorReview, + pk=author_review_id, + assignment__article__id=article_id, + date_decided__isnull=True, + ) copyedit = author_review.assignment try: @@ -792,26 +841,41 @@ def author_update_file(request, article_id, author_review_id, file_id): raise Http404 if request.POST and request.FILES: - - if 'replacement' in request.POST: - uploaded_file = request.FILES.get('replacement-file') - label = request.POST.get('label') - new_file = files.save_file_to_article(uploaded_file, copyedit.article, request.user, - replace=file, is_galley=False, label=label) - files.replace_file(copyedit.article, file, new_file, copyedit=copyedit, retain_old_label=False) + if "replacement" in request.POST: + uploaded_file = request.FILES.get("replacement-file") + label = request.POST.get("label") + new_file = files.save_file_to_article( + uploaded_file, + copyedit.article, + request.user, + replace=file, + is_galley=False, + label=label, + ) + files.replace_file( + copyedit.article, + file, + new_file, + copyedit=copyedit, + retain_old_label=False, + ) author_review.files_updated.add(new_file) - return redirect(reverse('author_copyedit', - kwargs={'article_id': article_id, 'author_review_id': author_review.pk})) + return redirect( + reverse( + "author_copyedit", + kwargs={"article_id": article_id, "author_review_id": author_review.pk}, + ) + ) - if request.GET.get('file_id'): + if request.GET.get("file_id"): return logic.attempt_to_serve_file(request, copyedit) - template = 'copyediting/author_update_file.html' + template = "copyediting/author_update_file.html" context = { - 'author_review': author_review, - 'copyedit': copyedit, - 'file': file, + "author_review": author_review, + "copyedit": copyedit, + "file": file, } return render(request, template, context) diff --git a/src/core/__init__.py b/src/core/__init__.py index 0b8a5f3bcb..8b7aaf8c34 100755 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -3,4 +3,4 @@ __license__ = "AGPL v3" __maintainer__ = "Birkbeck Centre for Technology and Publishing" -default_app_config = 'core.apps.CoreConfig' +default_app_config = "core.apps.CoreConfig" diff --git a/src/core/admin.py b/src/core/admin.py index d93dfc381c..258ea5818e 100755 --- a/src/core/admin.py +++ b/src/core/admin.py @@ -16,59 +16,116 @@ class AccountRoleAdmin(admin.ModelAdmin): - list_display = ('user', 'role', 'journal') - list_filter = ('journal', 'role') - search_fields = ('user__first_name', 'user__last_name', - 'user__email', 'user__orcid', - 'role__slug', 'role__name', 'journal__code') - raw_id_fields = ('user',) + list_display = ("user", "role", "journal") + list_filter = ("journal", "role") + search_fields = ( + "user__first_name", + "user__last_name", + "user__email", + "user__orcid", + "role__slug", + "role__name", + "journal__code", + ) + raw_id_fields = ("user",) class SettingAdmin(admin.ModelAdmin): """Displays Setting objects in the Django admin interface.""" - list_display = ('pk', 'name', 'pretty_name', 'group', - 'types', 'is_translatable') - list_filter = ('group', 'types', 'is_translatable') - search_fields = ('name', 'pretty_name', 'description') - list_display_links = ('name', 'pretty_name',) - inlines = [ - admin_utils.SettingValueInline - ] + list_display = ("pk", "name", "pretty_name", "group", "types", "is_translatable") + list_filter = ("group", "types", "is_translatable") + search_fields = ("name", "pretty_name", "description") + list_display_links = ( + "name", + "pretty_name", + ) + + inlines = [admin_utils.SettingValueInline] class AccountAdmin(UserAdmin): """Displays Account objects in the Django admin interface.""" - list_display = ('id', 'email', 'orcid', 'first_name', 'middle_name', - 'last_name', 'institution', '_roles_in', 'last_login') - list_display_links = ('id', 'email') - list_filter = ('accountrole__journal', - 'repositoryrole__repository__short_name', - 'is_active', 'is_staff', 'is_admin', 'is_superuser', - 'last_login') - search_fields = ('id', 'username', 'email', 'first_name', 'middle_name', - 'last_name', 'orcid', 'institution', - 'biography', 'signature') + + list_display = ( + "id", + "email", + "orcid", + "first_name", + "middle_name", + "last_name", + "institution", + "_roles_in", + "last_login", + ) + list_display_links = ("id", "email") + list_filter = ( + "accountrole__journal", + "repositoryrole__repository__short_name", + "is_active", + "is_staff", + "is_admin", + "is_superuser", + "last_login", + ) + search_fields = ( + "id", + "username", + "email", + "first_name", + "middle_name", + "last_name", + "orcid", + "institution", + "biography", + "signature", + ) fieldsets = UserAdmin.fieldsets + ( - (None, {'fields': ( - 'name_prefix', 'middle_name', 'orcid', - 'institution', 'department', 'country', 'twitter', - 'linkedin', 'facebook', 'github', 'website', 'biography', 'enable_public_profile', - 'signature', 'profile_image', 'interest', "preferred_timezone", - )}), + ( + None, + { + "fields": ( + "name_prefix", + "middle_name", + "orcid", + "institution", + "department", + "country", + "twitter", + "linkedin", + "facebook", + "github", + "website", + "biography", + "enable_public_profile", + "signature", + "profile_image", + "interest", + "preferred_timezone", + ) + }, + ), ) add_form = forms.UserCreationFormExtended add_fieldsets = ( - (None, { - 'classes': ('wide',), - 'fields': ('email', 'first_name', 'last_name', - 'password1', 'password2',) - }), + ( + None, + { + "classes": ("wide",), + "fields": ( + "email", + "first_name", + "last_name", + "password1", + "password2", + ), + }, + ), ) - raw_id_fields = ('interest',) + raw_id_fields = ("interest",) inlines = [ admin_utils.AccountRoleInline, @@ -86,136 +143,207 @@ def _roles_in(self, obj): repositories = repository_models.Repository.objects.filter( repositoryrole__user=obj, ).distinct() - return ', '.join( - [str(journal) for journal in journals] + - [repository.short_name for repository in repositories] + return ", ".join( + [str(journal) for journal in journals] + + [repository.short_name for repository in repositories] ) else: - return '' + return "" class RoleAdmin(admin.ModelAdmin): """Displays Role objects in the Django admin interface.""" - list_display = ('slug', 'name') - search_fields = ('slug', 'name',) + + list_display = ("slug", "name") + search_fields = ( + "slug", + "name", + ) class PasswordResetAdmin(admin.ModelAdmin): """Displays Password Reset Data""" - list_display = ('account', 'expiry', 'expired') - search_fields = ('account__first_name', 'account__last_name', - 'account__orcid', 'account__email') - list_filter = ('expired', 'expiry') - raw_id_fields = ('account',) - date_hierarchy = ('expiry') + + list_display = ("account", "expiry", "expired") + search_fields = ( + "account__first_name", + "account__last_name", + "account__orcid", + "account__email", + ) + list_filter = ("expired", "expiry") + raw_id_fields = ("account",) + date_hierarchy = "expiry" class SettingValueAdmin(admin.ModelAdmin): - list_display = ('_setting_name', 'value', 'journal') + list_display = ("_setting_name", "value", "journal") list_filter = ( - 'journal', - 'setting__types', + "journal", + "setting__types", ) search_fields = ( - 'setting__name', - 'setting__pretty_name', - 'setting__group__name', - 'setting__types', - 'value', + "setting__name", + "setting__pretty_name", + "setting__group__name", + "setting__types", + "value", ) raw_id_fields = ( - 'setting', 'journal', + "setting", + "journal", ) @staticmethod def apply_select_related(self, qs): - return qs.prefetch_related('journal', 'setting') + return qs.prefetch_related("journal", "setting") def _setting_name(self, obj): - return obj.setting.name if obj else '' + return obj.setting.name if obj else "" class CountryAdmin(admin.ModelAdmin): - list_display = ('code', 'name') - search_fields = ('code', 'name') + list_display = ("code", "name") + search_fields = ("code", "name") class HomepageElementAdmin(admin.ModelAdmin): """Displays Setting objects in the Django admin interface.""" - list_display = ('name', 'active', 'object', 'sequence', 'configure_url', - 'template_path', 'available_to_press', 'has_config') - list_filter = (admin_utils.GenericRelationJournalFilter, - admin_utils.GenericRelationPressFilter, - 'name', 'active', 'has_config', 'configure_url', - 'template_path') - search_fields = ('name',) + + list_display = ( + "name", + "active", + "object", + "sequence", + "configure_url", + "template_path", + "available_to_press", + "has_config", + ) + list_filter = ( + admin_utils.GenericRelationJournalFilter, + admin_utils.GenericRelationPressFilter, + "name", + "active", + "has_config", + "configure_url", + "template_path", + ) + search_fields = ("name",) class FileAdmin(admin.ModelAdmin): """displays files""" - list_display = ('id', 'original_filename', 'last_modified', 'label', - 'owner', 'article_pk', '_journal') - list_display_links = ('original_filename',) - list_filter = (admin_utils.ArticleIDJournalFilter, - 'last_modified', 'date_uploaded', 'mime_type', - 'is_galley') - search_fields = ('original_filename', 'article_id', 'label', - 'description', 'owner__email', 'owner__first_name', - 'owner__last_name') - raw_id_fields = ('owner', 'history') - readonly_fields = ['article_id'] + + list_display = ( + "id", + "original_filename", + "last_modified", + "label", + "owner", + "article_pk", + "_journal", + ) + list_display_links = ("original_filename",) + list_filter = ( + admin_utils.ArticleIDJournalFilter, + "last_modified", + "date_uploaded", + "mime_type", + "is_galley", + ) + search_fields = ( + "original_filename", + "article_id", + "label", + "description", + "owner__email", + "owner__first_name", + "owner__last_name", + ) + raw_id_fields = ("owner", "history") + readonly_fields = ["article_id"] def article_pk(self, obj): if obj and obj.article_id: - link = '{pk}'.format(pk=obj.article_id) + link = '{pk}'.format( + pk=obj.article_id + ) return mark_safe(link) else: - return '-' + return "-" def _journal(self, obj): if obj and obj.article_id: - return journal_models.Journal.objects.get( - article__pk=obj.article_id - ).code + return journal_models.Journal.objects.get(article__pk=obj.article_id).code class FileHistoryAdmin(admin.ModelAdmin): """displays file history objects""" - list_display = ('id', 'original_filename', 'label', - 'owner', 'history_seq', 'article_id') - list_filter = ('mime_type',) - search_fields = ('original_filename', 'article_id', 'label', - 'description', 'owner__first_name', - 'owner__last_name', 'owner__email') - raw_id_fields = ('owner',) + + list_display = ( + "id", + "original_filename", + "label", + "owner", + "history_seq", + "article_id", + ) + list_filter = ("mime_type",) + search_fields = ( + "original_filename", + "article_id", + "label", + "description", + "owner__first_name", + "owner__last_name", + "owner__email", + ) + raw_id_fields = ("owner",) class XSLFileAdmin(admin.ModelAdmin): """Displays Setting objects in the Django admin interface.""" - list_display = ('label', 'date_uploaded', 'file', - 'original_filename', 'journal', '_comments') - list_filter = ('journal', 'date_uploaded', 'label') - search_fields = ('label', 'original_filename', 'comments') - date_hierarchy = ('date_uploaded') + + list_display = ( + "label", + "date_uploaded", + "file", + "original_filename", + "journal", + "_comments", + ) + list_filter = ("journal", "date_uploaded", "label") + search_fields = ("label", "original_filename", "comments") + date_hierarchy = "date_uploaded" def _comments(self, obj): - return truncatewords(obj.comments, 10) if obj else '' + return truncatewords(obj.comments, 10) if obj else "" class SupplementaryFileAdmin(admin.ModelAdmin): - list_display = ('label', 'file', '_date_uploaded', '_last_modified', - 'doi', 'mime_type', '_journal') - list_filter = (admin_utils.FileArticleIDJournalFilter, - 'file__date_uploaded', - 'file__last_modified', - 'file__mime_type') - search_fields = ('file__label', 'doi', 'file__original_filename') + list_display = ( + "label", + "file", + "_date_uploaded", + "_last_modified", + "doi", + "mime_type", + "_journal", + ) + list_filter = ( + admin_utils.FileArticleIDJournalFilter, + "file__date_uploaded", + "file__last_modified", + "file__mime_type", + ) + search_fields = ("file__label", "doi", "file__original_filename") def _date_uploaded(self, obj): - return obj.file.date_uploaded if obj else '' + return obj.file.date_uploaded if obj else "" def _last_modified(self, obj): - return obj.file.last_modified if obj else '' + return obj.file.last_modified if obj else "" def _journal(self, obj): if obj and obj.file.article_id: @@ -225,19 +353,25 @@ def _journal(self, obj): class InterestAdmin(admin.ModelAdmin): - list_display = ('pk', 'name', '_journals') - list_display_links = ('pk', 'name',) - list_filter = ('account__accountrole__journal',) - search_fields = ('pk', 'name',) + list_display = ("pk", "name", "_journals") + list_display_links = ( + "pk", + "name", + ) + list_filter = ("account__accountrole__journal",) + search_fields = ( + "pk", + "name", + ) def _journals(self, obj): if obj and obj.account_set: journals = journal_models.Journal.objects.filter( accountrole__user__interest=obj, ).distinct() - return ', '.join([journal.code for journal in journals]) + return ", ".join([journal.code for journal in journals]) else: - return '' + return "" inlines = [ admin_utils.AccountInterestInline, @@ -245,163 +379,257 @@ def _journals(self, obj): class TaskAdmin(admin.ModelAdmin): - list_display = ('pk', 'title', 'object', 'completed_by', - 'created', 'due', 'completed') - list_display_links = ('pk', 'title',) - list_filter = (admin_utils.GenericRelationArticleJournalFilter, - admin_utils.GenericRelationPreprintRepositoryFilter, - 'title', 'created', 'due', 'completed') - search_fields = ('pk', 'title', 'link', 'completed_by__first_name', - 'completed_by__last_name', 'completed_by__email') + list_display = ( + "pk", + "title", + "object", + "completed_by", + "created", + "due", + "completed", + ) + list_display_links = ( + "pk", + "title", + ) + list_filter = ( + admin_utils.GenericRelationArticleJournalFilter, + admin_utils.GenericRelationPreprintRepositoryFilter, + "title", + "created", + "due", + "completed", + ) + search_fields = ( + "pk", + "title", + "link", + "completed_by__first_name", + "completed_by__last_name", + "completed_by__email", + ) class TaskCompleteEventsAdmin(admin.ModelAdmin): - list_display = ('pk', 'event_name', ) - list_display_links = ('pk', 'event_name',) + list_display = ( + "pk", + "event_name", + ) + list_display_links = ( + "pk", + "event_name", + ) class WorkflowAdmin(admin.ModelAdmin): - list_display = ('journal',) - raw_id_fields = ('elements',) + list_display = ("journal",) + raw_id_fields = ("elements",) class WorkflowElementAdmin(admin.ModelAdmin): - list_display = ('element_name', 'journal', 'handshake_url', - 'jump_url', 'stage', 'order') - list_filter = ('journal', 'element_name', 'stage') - search_fields = ('journal__code', 'element_name', 'stage') + list_display = ( + "element_name", + "journal", + "handshake_url", + "jump_url", + "stage", + "order", + ) + list_filter = ("journal", "element_name", "stage") + search_fields = ("journal__code", "element_name", "stage") class WorkflowLogAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', '_workflow_element', - '_stage', 'timestamp') - list_filter = ('element__journal', 'element__element_name', - 'timestamp',) - search_fields = ('article__title', 'element__element_name', - 'element__stage', 'element__journal__code') - date_hierarchy = ('timestamp') - raw_id_fields = ('article',) + list_display = ( + "pk", + "_article", + "_journal", + "_workflow_element", + "_stage", + "timestamp", + ) + list_filter = ( + "element__journal", + "element__element_name", + "timestamp", + ) + search_fields = ( + "article__title", + "element__element_name", + "element__stage", + "element__journal__code", + ) + date_hierarchy = "timestamp" + raw_id_fields = ("article",) def _stage(self, obj): - return obj.element.stage if obj else '' + return obj.element.stage if obj else "" def _workflow_element(self, obj): - return obj.element.element_name if obj else '' + return obj.element.element_name if obj else "" class OrcidTokenAdmin(admin.ModelAdmin): - list_display = ('token', 'orcid', 'expiry') - list_filter = ('expiry', ) - search_fields = ('orcid', ) - date_hierarchy = ('expiry') + list_display = ("token", "orcid", "expiry") + list_filter = ("expiry",) + search_fields = ("orcid",) + date_hierarchy = "expiry" class SettingGroupAdmin(admin.ModelAdmin): - list_display = ('pk', 'name', 'enabled') - list_filter = ('enabled',) - search_fields = ('name',) - list_display_links = ('name',) + list_display = ("pk", "name", "enabled") + list_filter = ("enabled",) + search_fields = ("name",) + list_display_links = ("name",) class GalleyAdmin(admin.ModelAdmin): - list_display = ('pk', 'label', 'last_modified', 'type', - 'article_pk', 'file_link', 'file', '_journal') - list_filter = (admin_utils.ArticleIDJournalFilter, - 'last_modified', 'type', 'is_remote', 'public') - search_fields = ('label', 'article__title', 'file__original_filename', - 'article__id') - raw_id_fields = ('article', 'file', 'css_file') - filter_horizontal = ('images',) - date_hierarchy = ('last_modified') + list_display = ( + "pk", + "label", + "last_modified", + "type", + "article_pk", + "file_link", + "file", + "_journal", + ) + list_filter = ( + admin_utils.ArticleIDJournalFilter, + "last_modified", + "type", + "is_remote", + "public", + ) + search_fields = ( + "label", + "article__title", + "file__original_filename", + "article__id", + ) + raw_id_fields = ("article", "file", "css_file") + filter_horizontal = ("images",) + date_hierarchy = "last_modified" def article_pk(self, obj): if obj and obj.article_id: - link = '{pk}'.format(pk=obj.article_id) + link = '{pk}'.format( + pk=obj.article_id + ) return mark_safe(link) else: - return '-' + return "-" def file_link(self, obj): if obj and obj.file: - link = '{pk}'.format(pk=obj.file.pk) + link = '{pk}'.format( + pk=obj.file.pk + ) return mark_safe(link) else: - return '-' + return "-" def _journal(self, obj): if obj and obj.article_id: - return journal_models.Journal.objects.get( - article__pk=obj.article_id - ).code + return journal_models.Journal.objects.get(article__pk=obj.article_id).code class EditorialGroupAdmin(admin.ModelAdmin): - list_display = ('name', 'journal', 'press', 'sequence', - 'display_profile_images') - list_filter = ('journal', 'press') - search_fields = ('name',) + list_display = ("name", "journal", "press", "sequence", "display_profile_images") + list_filter = ("journal", "press") + search_fields = ("name",) - inlines = [ - admin_utils.EditorialGroupMemberInline - ] + inlines = [admin_utils.EditorialGroupMemberInline] class EditorialMemberAdmin(admin.ModelAdmin): - list_display = ('pk', 'user', 'group', '_journal', 'sequence') - list_filter = ('group__journal', 'group') - raw_id_fields = ('user', ) - search_fields = ('pk', 'user__first_name', 'user__last_name', - 'user__email', 'group__name', 'group__description') + list_display = ("pk", "user", "group", "_journal", "sequence") + list_filter = ("group__journal", "group") + raw_id_fields = ("user",) + search_fields = ( + "pk", + "user__first_name", + "user__last_name", + "user__email", + "group__name", + "group__description", + ) def _journal(self, obj): - return obj.group.journal if obj else '' + return obj.group.journal if obj else "" class ContactsAdmin(admin.ModelAdmin): - list_display = ('name', 'email', 'role', 'object', 'sequence') - list_filter = (admin_utils.GenericRelationJournalFilter, - admin_utils.GenericRelationPressFilter) - search_fields = ('name', 'email', 'role') + list_display = ("name", "email", "role", "object", "sequence") + list_filter = ( + admin_utils.GenericRelationJournalFilter, + admin_utils.GenericRelationPressFilter, + ) + search_fields = ("name", "email", "role") class ContactAdmin(admin.ModelAdmin): - list_display = ('subject', 'sender', 'recipient', - 'client_ip', 'date_sent', 'object') - list_filter = (admin_utils.GenericRelationJournalFilter, - admin_utils.GenericRelationPressFilter, - 'date_sent', 'recipient') - search_fields = ('subject', 'sender', 'recipient',) - date_hierarchy = ('date_sent') + list_display = ( + "subject", + "sender", + "recipient", + "client_ip", + "date_sent", + "object", + ) + list_filter = ( + admin_utils.GenericRelationJournalFilter, + admin_utils.GenericRelationPressFilter, + "date_sent", + "recipient", + ) + search_fields = ( + "subject", + "sender", + "recipient", + ) + date_hierarchy = "date_sent" class DomainAliasAdmin(admin.ModelAdmin): - list_display = ('domain', 'redirect', 'site_object', 'redirect_url') - list_filter = ('journal', 'press') - search_fields = ('domain',) + list_display = ("domain", "redirect", "site_object", "redirect_url") + list_filter = ("journal", "press") + search_fields = ("domain",) class LoginAttemptAdmin(admin.ModelAdmin): - list_display = ('ip_address', 'user_agent', 'timestamp') - list_filter = ('timestamp',) - search_fields = ('ip_address', 'user_agent',) - date_hierarchy = ('timestamp') + list_display = ("ip_address", "user_agent", "timestamp") + list_filter = ("timestamp",) + search_fields = ( + "ip_address", + "user_agent", + ) + date_hierarchy = "timestamp" class AccessRequestAdmin(admin.ModelAdmin): - list_display = ('user', 'journal', 'repository', 'role', - 'requested', 'processed') - list_filter = ('journal', 'repository', 'role', 'processed') - search_fields = ('user__first_name', 'user__last_name', 'user__email', - 'journal__code', 'repository__short_name', 'role__slug', - 'role__name') - raw_id_fields = ('user',) - date_hierarchy = ('requested') + list_display = ("user", "journal", "repository", "role", "requested", "processed") + list_filter = ("journal", "repository", "role", "processed") + search_fields = ( + "user__first_name", + "user__last_name", + "user__email", + "journal__code", + "repository__short_name", + "role__slug", + "role__name", + ) + raw_id_fields = ("user",) + date_hierarchy = "requested" admin_list = [ (models.AccountRole, AccountRoleAdmin), (models.Account, AccountAdmin), - (models.Role, RoleAdmin,), + ( + models.Role, + RoleAdmin, + ), (models.Setting, SettingAdmin), (models.SettingGroup, SettingGroupAdmin), (models.SettingValue, SettingValueAdmin), diff --git a/src/core/apps.py b/src/core/apps.py index c0370a8162..1403819c28 100755 --- a/src/core/apps.py +++ b/src/core/apps.py @@ -9,9 +9,11 @@ class CoreConfig(AppConfig): """Configures the core app.""" - name = 'core' + + name = "core" def ready(self): from core.model_utils import SearchLookup + models.CharField.register_lookup(SearchLookup) models.TextField.register_lookup(SearchLookup) diff --git a/src/core/context_processors.py b/src/core/context_processors.py index 80edf1c54b..90ae77d9c6 100755 --- a/src/core/context_processors.py +++ b/src/core/context_processors.py @@ -16,7 +16,7 @@ def journal(request): :param request: the active request :return: dictionary containing a journal object under key 'journal' or None if this is a press site """ - return {'journal': request.journal} + return {"journal": request.journal} def press(request): @@ -28,9 +28,10 @@ def press(request): :return: dictionary containing a press object under key 'press' """ return { - 'press': request.press, - 'display_preprint_editors': request.press.get_setting_value( - 'Display Preprint Editors') + "press": request.press, + "display_preprint_editors": request.press.get_setting_value( + "Display Preprint Editors" + ), } @@ -43,7 +44,7 @@ def journal_settings(request): :return: dictionary containing a dictionary of journal settings under key 'journal_settings' """ - return {'journal_settings': logic.settings_for_context(request)} + return {"journal_settings": logic.settings_for_context(request)} def active(request): @@ -55,10 +56,10 @@ def active(request): :return: the active path that corresponds to this request or an empty string if at root """ try: - url_list = request.path.split('/') - return {'active': url_list[1]} + url_list = request.path.split("/") + return {"active": url_list[1]} except (IndexError, AttributeError): - return {'active': ''} + return {"active": ""} def navigation(request): @@ -68,11 +69,13 @@ def navigation(request): :param request: the active request :return: the active path that corresponds to this request or an empty string if at root """ - top_nav_items = cms_models.NavigationItem.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk, - top_level_nav__isnull=True).order_by('sequence') + top_nav_items = cms_models.NavigationItem.objects.filter( + content_type=request.model_content_type, + object_id=request.site_type.pk, + top_level_nav__isnull=True, + ).order_by("sequence") - return {'navigation_items': top_nav_items} + return {"navigation_items": top_nav_items} def version(request): @@ -82,4 +85,4 @@ def version(request): :param request: an HttpRequest object :return: a dictionary containing the current version. """ - return {'version': get_janeway_version()} + return {"version": get_janeway_version()} diff --git a/src/core/dev_settings.py b/src/core/dev_settings.py index bca0e0ae0c..75bfdf66b9 100644 --- a/src/core/dev_settings.py +++ b/src/core/dev_settings.py @@ -1,23 +1,27 @@ # SECURITY WARNING: keep the secret key used in production secret! # You should change this key before you go live! import os + DEBUG = True -SECRET_KEY = 'uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv' +SECRET_KEY = "uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv" # This is the default redirect if no other sites are found. -DEFAULT_HOST = 'https://www.example.org' -EMAIL_BACKEND = os.environ.get( - 'JANEWAY_EMAIL_BACKEND', -) or 'django.core.mail.backends.console.EmailBackend' +DEFAULT_HOST = "https://www.example.org" +EMAIL_BACKEND = ( + os.environ.get( + "JANEWAY_EMAIL_BACKEND", + ) + or "django.core.mail.backends.console.EmailBackend" +) -URL_CONFIG = 'path' # path or domain +URL_CONFIG = "path" # path or domain MIDDLEWARE = ( - 'utils.middleware.TimeMonitoring', - 'debug_toolbar.middleware.DebugToolbarMiddleware', + "utils.middleware.TimeMonitoring", + "debug_toolbar.middleware.DebugToolbarMiddleware", ) INSTALLED_APPS = [ - 'debug_toolbar', + "debug_toolbar", ] @@ -33,57 +37,57 @@ def show_toolbar(request): "SHOW_TOOLBAR_CALLBACK": show_toolbar, } -HIJACK_LOGIN_REDIRECT_URL = '/manager/' +HIJACK_LOGIN_REDIRECT_URL = "/manager/" HIJACK_USERS_ENABLED = True ENABLE_FULL_TEXT_SEARCH = True LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'root': { - 'level': 'DEBUG', - 'handlers': ['console', 'log_file'], + "version": 1, + "disable_existing_loggers": False, + "root": { + "level": "DEBUG", + "handlers": ["console", "log_file"], }, - 'formatters': { - 'default': { - 'format': '%(levelname)s %(asctime)s %(module)s ' - 'P:%(process)d T:%(thread)d %(message)s', + "formatters": { + "default": { + "format": "%(levelname)s %(asctime)s %(module)s " + "P:%(process)d T:%(thread)d %(message)s", }, - 'coloured': { - '()': 'colorlog.ColoredFormatter', - 'format': '%(log_color)s%(levelname)s %(asctime)s %(module)s ' - 'P:%(process)d T:%(thread)d %(message)s', - 'log_colors': { - 'DEBUG': 'green', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'red', - } + "coloured": { + "()": "colorlog.ColoredFormatter", + "format": "%(log_color)s%(levelname)s %(asctime)s %(module)s " + "P:%(process)d T:%(thread)d %(message)s", + "log_colors": { + "DEBUG": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red", + }, }, }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'coloured', - 'stream': 'ext://sys.stdout', + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "coloured", + "stream": "ext://sys.stdout", }, - 'log_file': { - 'level': 'DEBUG', - 'class': 'logging.handlers.RotatingFileHandler', - 'maxBytes': 1024*1024*50, # 50 MB - 'backupCount': 1, - 'filename': os.path.join(PROJECT_DIR , 'logs/janeway.log'), - 'formatter': 'default' + "log_file": { + "level": "DEBUG", + "class": "logging.handlers.RotatingFileHandler", + "maxBytes": 1024 * 1024 * 50, # 50 MB + "backupCount": 1, + "filename": os.path.join(PROJECT_DIR, "logs/janeway.log"), + "formatter": "default", }, }, - 'loggers': { - 'django.db.backends': { + "loggers": { + "django.db.backends": { #'level': 'DEBUG', - 'level': 'WARNING', - 'handlers': ['console', 'log_file'], - 'propagate': False, + "level": "WARNING", + "handlers": ["console", "log_file"], + "propagate": False, }, }, } diff --git a/src/core/email.py b/src/core/email.py index fea5a39b81..d554483273 100644 --- a/src/core/email.py +++ b/src/core/email.py @@ -6,6 +6,7 @@ from utils import notify_helpers + @dataclass(frozen=True) class EmailData: subject: str @@ -24,7 +25,7 @@ def send_email( preprint=None, log_dict=None, ): - """ A standard way to send email using data from core.forms.EmailForm. + """A standard way to send email using data from core.forms.EmailForm. :param user: The main recipient of the email. Can be None if email_data has recipient :type user: Account or NoneType @@ -41,17 +42,17 @@ def send_email( elif email_data.to: to = email_data.to else: - raise ImproperlyConfigured('Pass a user or email_data with a to field') + raise ImproperlyConfigured("Pass a user or email_data with a to field") subject = email_data.subject message = email_data.body if not log_dict: target = article or preprint or None log_dict = { - 'level': 'Info', - 'action_text': 'Contact User', - 'types': 'Email', - 'target': target + "level": "Info", + "action_text": "Contact User", + "types": "Email", + "target": target, } notify_helpers.send_email_with_body_from_user( diff --git a/src/core/error_views.py b/src/core/error_views.py index 7b5771931e..942e841168 100644 --- a/src/core/error_views.py +++ b/src/core/error_views.py @@ -2,7 +2,7 @@ def handler404(request, *args, **argv): - template = '404.html' + template = "404.html" context = {} return render( request, @@ -15,7 +15,7 @@ def handler404(request, *args, **argv): def handler500(request, *args, **argv): response = render( None, - '500.html', + "500.html", ) response.status_code = 404 return response diff --git a/src/core/example_settings.py b/src/core/example_settings.py index 629c1f6972..397b13a127 100644 --- a/src/core/example_settings.py +++ b/src/core/example_settings.py @@ -1,68 +1,67 @@ - # SECURITY WARNING: keep the secret key used in production secret! # You should change this key before you go live! -SECRET_KEY = 'uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv' +SECRET_KEY = "uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv" # This is the default redirect if no other sites are found. -DEFAULT_HOST = 'https://www.example.org' -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -LOGIN_REDIRECT_URL = '/user/profile/' +DEFAULT_HOST = "https://www.example.org" +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +LOGIN_REDIRECT_URL = "/user/profile/" # CATCHA_TYPE should be either 'simple_math', 'recaptcha' or 'hcaptcha' to enable captcha -#fields, otherwise disabled -CAPTCHA_TYPE = 'recaptcha' +# fields, otherwise disabled +CAPTCHA_TYPE = "recaptcha" # If using recaptcha complete the following -RECAPTCHA_PRIVATE_KEY = '' -RECAPTCHA_PUBLIC_KEY = '' +RECAPTCHA_PRIVATE_KEY = "" +RECAPTCHA_PUBLIC_KEY = "" # If using hcaptcha complete the following: -HCAPTCHA_SITEKEY = '' -HCAPTCHA_SECRET = '' +HCAPTCHA_SITEKEY = "" +HCAPTCHA_SECRET = "" # ORCID Settings ENABLE_ORCID = True -ORCID_API_URL = 'http://pub.orcid.org/v1.2_rc7/' -ORCID_URL = 'https://orcid.org/oauth/authorize' -ORCID_TOKEN_URL = 'https://pub.orcid.org/oauth/token' -ORCID_CLIENT_SECRET = '' -ORCID_CLIENT_ID = '' +ORCID_API_URL = "http://pub.orcid.org/v1.2_rc7/" +ORCID_URL = "https://orcid.org/oauth/authorize" +ORCID_TOKEN_URL = "https://pub.orcid.org/oauth/token" +ORCID_CLIENT_SECRET = "" +ORCID_CLIENT_ID = "" # Default Langague -LANGUAGE_CODE = 'en' +LANGUAGE_CODE = "en" -URL_CONFIG = 'path' # path or domain +URL_CONFIG = "path" # path or domain DATABASES = { - 'default': { - #Example ENGINEs: + "default": { + # Example ENGINEs: # sqlite: 'django.db.backends.sqlite # mysql: 'django.db.backends.mysql # postgres: 'django.db.backends.postgresql - 'ENGINE': 'django.db.backends.mysql', - 'NAME': '', - 'USER': '', - 'PASSWORD': '', - 'HOST': '', - 'PORT': '', - 'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB'}, + "ENGINE": "django.db.backends.mysql", + "NAME": "", + "USER": "", + "PASSWORD": "", + "HOST": "", + "PORT": "", + "OPTIONS": {"init_command": "SET default_storage_engine=INNODB"}, } } # OIDC Settings ENABLE_OIDC = False -OIDC_SERVICE_NAME = 'OIDC Service Name' -OIDC_RP_CLIENT_ID = '' -OIDC_RP_CLIENT_SECRET = '' -OIDC_RP_SIGN_ALGO = 'RS256' +OIDC_SERVICE_NAME = "OIDC Service Name" +OIDC_RP_CLIENT_ID = "" +OIDC_RP_CLIENT_SECRET = "" +OIDC_RP_SIGN_ALGO = "RS256" OIDC_OP_AUTHORIZATION_ENDPOINT = "" OIDC_OP_TOKEN_ENDPOINT = "" OIDC_OP_USER_ENDPOINT = "" -OIDC_OP_JWKS_ENDPOINT = '' +OIDC_OP_JWKS_ENDPOINT = "" -ENABLE_FULL_TEXT_SEARCH = False # Read the docs before enabling full text +ENABLE_FULL_TEXT_SEARCH = False # Read the docs before enabling full text # Model used for indexing full text files CORE_FILETEXT_MODEL = "core.FileText" # Use "core.PGFileText" for Postgres diff --git a/src/core/fields.py b/src/core/fields.py index 5a801b0056..346d6974c5 100755 --- a/src/core/fields.py +++ b/src/core/fields.py @@ -2,12 +2,13 @@ class IntegerRangeField(models.IntegerField): - - def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs): + def __init__( + self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs + ): self.min_value, self.max_value = min_value, max_value models.IntegerField.__init__(self, verbose_name, name, **kwargs) def formfield(self, **kwargs): - defaults = {'min_value': self.min_value, 'max_value': self.max_value} + defaults = {"min_value": self.min_value, "max_value": self.max_value} defaults.update(kwargs) return super(IntegerRangeField, self).formfield(**defaults) diff --git a/src/core/file_system.py b/src/core/file_system.py index 2f5e04cf8c..333971a845 100644 --- a/src/core/file_system.py +++ b/src/core/file_system.py @@ -1,6 +1,7 @@ """ Janeway's File System classes """ + __copyright__ = "Copyright 2018 Birkbeck, University of London" __author__ = "Birkbeck Centre for Technology and Publishing" __license__ = "AGPL v3" @@ -15,10 +16,11 @@ @deconstructible class JanewayFileSystemStorage(FileSystemStorage): - """ Allows to change settings.MEDIA_ROOT without generating a migration + """Allows to change settings.MEDIA_ROOT without generating a migration :param relative_path: relative path on top of settings.MEDIA_ROOT """ + def __init__(self, location=None, *args, **kwargs): relative_path = None if location: @@ -32,7 +34,4 @@ def __init__(self, location=None, *args, **kwargs): super().__init__(location, *args, **kwargs) def __eq__(self, other): - return ( - isinstance(other, self.__class__) - and self.base_url == other.base_url - ) + return isinstance(other, self.__class__) and self.base_url == other.base_url diff --git a/src/core/files.py b/src/core/files.py index 1cd9613f70..168fde1c0e 100755 --- a/src/core/files.py +++ b/src/core/files.py @@ -34,39 +34,36 @@ logger = get_logger(__name__) -TEMP_DIR = os.path.join(settings.BASE_DIR, 'files', 'temp') +TEMP_DIR = os.path.join(settings.BASE_DIR, "files", "temp") EDITABLE_FORMAT = ( - 'application/rtf', - 'application/x-rtf', - 'text/richtext', - 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'application/vnd.oasis.opendocument.text', + "application/rtf", + "application/x-rtf", + "text/richtext", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.oasis.opendocument.text", ) IMAGE_MIMETYPES = ( - 'image/jpeg', - 'image/jpg', - 'image/png', - 'image/gif', - 'image/tiff', - 'image/avif', + "image/jpeg", + "image/jpg", + "image/png", + "image/gif", + "image/tiff", + "image/avif", ) XML_MIMETYPES = ( - 'application/xml', - 'text/xml', + "application/xml", + "text/xml", ) -HTML_MIMETYPES = ( - 'text/html', - 'application/xhtml+xml' -) +HTML_MIMETYPES = ("text/html", "application/xhtml+xml") PDF_MIMETYPES = { - 'application/pdf', - 'application/x-pdf', + "application/pdf", + "application/x-pdf", } @@ -91,13 +88,13 @@ def create_temp_file(content, filename): :param filename: Filename of the temp file :return: Temp file's path eg /src/files/temp/temp.txt """ - filename = '{uuid}-{filename}'.format(uuid=uuid4(), filename=filename) + filename = "{uuid}-{filename}".format(uuid=uuid4(), filename=filename) # Check if the temp folder exists and, if not, create it. mkdirs(TEMP_DIR) filepath = os.path.join(TEMP_DIR, filename) - temp_file = open(filepath, 'w', encoding="utf-8") + temp_file = open(filepath, "w", encoding="utf-8") temp_file.write(content) temp_file.flush() temp_file.close() @@ -105,8 +102,16 @@ def create_temp_file(content, filename): return filepath -def copy_local_file_to_article(file_to_handle, file_name, article, owner, label=None, description=None, - replace=None, galley=False): +def copy_local_file_to_article( + file_to_handle, + file_name, + article, + owner, + label=None, + description=None, + replace=None, + galley=False, +): """Copy a local file into an article's folder with appropriate mime type and permissions. :param file_to_handle: the uploaded file object we need to handle @@ -123,13 +128,16 @@ def copy_local_file_to_article(file_to_handle, file_name, article, owner, label= # N.B. os.path.splitext[1] always returns the final file extension, even in a multi-dotted (.txt.html etc.) input filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id)) + folder_structure = os.path.join( + settings.BASE_DIR, "files", "articles", str(article.id) + ) copy_file_to_folder(file_to_handle, filename, folder_structure) file_mime = guess_mime(filename) from core import models + new_file = models.File( mime_type=file_mime, original_filename=original_filename, @@ -138,7 +146,7 @@ def copy_local_file_to_article(file_to_handle, file_name, article, owner, label= description=description, owner=owner, is_galley=galley, - article_id=article.pk + article_id=article.pk, ) new_file.save() @@ -146,8 +154,16 @@ def copy_local_file_to_article(file_to_handle, file_name, article, owner, label= return new_file -def save_file_to_article(file_to_handle, article, owner, label=None, description=None, replace=None, is_galley=False, - save=True): +def save_file_to_article( + file_to_handle, + article, + owner, + label=None, + description=None, + replace=None, + is_galley=False, + save=True, +): """Save a file into an article's folder with appropriate mime type and permissions. :param file_to_handle: the uploaded file object we need to handle @@ -166,7 +182,9 @@ def save_file_to_article(file_to_handle, article, owner, label=None, description # N.B. os.path.splitext[1] always returns the final file extension, even in a multi-dotted (.txt.html etc.) input filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id)) + folder_structure = os.path.join( + settings.BASE_DIR, "files", "articles", str(article.id) + ) if not os.path.exists(folder_structure): mkdirs(folder_structure) @@ -182,6 +200,7 @@ def save_file_to_article(file_to_handle, article, owner, label=None, description file_mime = guess_mime(filename) from core import models + new_file = models.File( mime_type=file_mime, original_filename=original_filename, @@ -190,7 +209,7 @@ def save_file_to_article(file_to_handle, article, owner, label=None, description description=description, owner=owner, is_galley=is_galley, - article_id=article.pk + article_id=article.pk, ) new_file.save() @@ -199,7 +218,7 @@ def save_file_to_article(file_to_handle, article, owner, label=None, description def guess_mime(filename): - """ Attempt to ascertain the MIME type of a file + """Attempt to ascertain the MIME type of a file :param filename: the filename of which to guess the type :return: the MIME type @@ -209,9 +228,9 @@ def guess_mime(filename): try: file_mime = file_mime[0] except IndexError: - file_mime = 'unknown' + file_mime = "unknown" if not file_mime: - file_mime = 'unknown' + file_mime = "unknown" return file_mime @@ -227,7 +246,7 @@ def check_in_memory_mime(in_memory_file): def copy_file_to_folder(file_to_handle, filename, folder_structure): - """ Copy a local file to the disk in the specified folder + """Copy a local file to the disk in the specified folder :param file_to_handle: the file itself :param filename: the filename to save as @@ -242,6 +261,7 @@ def copy_file_to_folder(file_to_handle, filename, folder_structure): # write the file to disk import shutil + shutil.copy(file_to_handle, path) return path @@ -254,17 +274,16 @@ def copy_article_file(article_to_copy_from, file_to_copy, article_to_copy_to): :param article_to_copy_to: An Article object :return: None """ - copy_to_folder_structure = os.path.join(settings.BASE_DIR, - 'files', - 'articles', - str(article_to_copy_to.id)) + copy_to_folder_structure = os.path.join( + settings.BASE_DIR, "files", "articles", str(article_to_copy_to.id) + ) file_path = os.path.join(copy_to_folder_structure, file_to_copy.uuid_filename) mkdirs(copy_to_folder_structure) shutil.copy(file_to_copy.get_file_path(article_to_copy_from), file_path) def save_file_to_disk(file_to_handle, filename, folder_structure, chunk=True): - """ Save a file to the disk in the specified folder + """Save a file to the disk in the specified folder :param file_to_handle: the file itself :param filename: the filename to save as @@ -278,7 +297,7 @@ def save_file_to_disk(file_to_handle, filename, folder_structure, chunk=True): path = os.path.join(folder_structure, str(filename)) # write the file to disk - with open(path, 'wb') as fd: + with open(path, "wb") as fd: if chunk: for chunk in file_to_handle.chunks(): fd.write(chunk) @@ -293,21 +312,27 @@ def get_file(file_to_get, article, as_bytes=False): :param article: the associated article :return: the contents of the file """ - path = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id), str(file_to_get.uuid_filename)) + path = os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(article.id), + str(file_to_get.uuid_filename), + ) if not os.path.isfile(path): return "" if as_bytes: - with open(path, 'rb') as contents: + with open(path, "rb") as contents: return contents.read() try: - with open(path, 'r') as content_file: + with open(path, "r") as content_file: content = content_file.read() return content except UnicodeDecodeError: - with open(path, 'r', encoding='utf-8') as content_file: + with open(path, "r", encoding="utf-8") as content_file: content = content_file.read() return content @@ -321,24 +346,34 @@ def render_xml(file_to_render, article, xsl_path=None, recover=False): :return: a transform of the file to through the XSLT processor """ - path = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id), str(file_to_render.uuid_filename)) + path = os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(article.id), + str(file_to_render.uuid_filename), + ) if not os.path.isfile(path): - util_models.LogEntry.add_entry(types='Error', - description='The required XML file for a transform {0} was not found'.format(path), - level='Error', actor=None, target=article) + util_models.LogEntry.add_entry( + types="Error", + description="The required XML file for a transform {0} was not found".format( + path + ), + level="Error", + actor=None, + target=article, + ) logger.debug("Bad/no file for XSLT transform") return "" if xsl_path is not None: - logger.debug('Rendering engine using {}'.format(xsl_path)) + logger.debug("Rendering engine using {}".format(xsl_path)) if not os.path.isfile(xsl_path): - logger.error( - 'The required XSLT file {} was not found'.format(xsl_path) - ) - xsl_path = os.path.join(settings.BASE_DIR, 'transform/xsl/default.xsl') - logger.debug('Rendering engine using {}'.format(xsl_path)) + logger.error("The required XSLT file {} was not found".format(xsl_path)) + xsl_path = os.path.join(settings.BASE_DIR, "transform/xsl/default.xsl") + logger.debug("Rendering engine using {}".format(xsl_path)) return transform_with_xsl(path, xsl_path, recover=recover) @@ -363,18 +398,19 @@ def transform_with_xsl(xml_path, xsl_path, recover=False): logger.error(xsl_error) if not recover: raise - return '' + return "" -def serve_any_file(request, file_to_serve, public=False, hide_name=False, - path_parts=()): +def serve_any_file( + request, file_to_serve, public=False, hide_name=False, path_parts=() +): # TODO: should rename to serve_file and the latter to serve_article_file # Or removed file_path = os.path.join( - settings.BASE_DIR, - 'files', - *(str(part) for part in path_parts), - str(file_to_serve.uuid_filename), + settings.BASE_DIR, + "files", + *(str(part) for part in path_parts), + str(file_to_serve.uuid_filename), ) try: return serve_file_to_browser( @@ -384,7 +420,9 @@ def serve_any_file(request, file_to_serve, public=False, hide_name=False, hide_name=hide_name, ) except IOError: - messages.add_message(request, messages.ERROR, 'File not found. {0}'.format(file_path)) + messages.add_message( + request, messages.ERROR, "File not found. {0}".format(file_path) + ) raise Http404 @@ -399,22 +437,17 @@ def serve_file(request, file_to_serve, article, public=False, hide_name=False): :return: a StreamingHttpResponse object with the requested file or an HttpResponseRedirect if there is an IO or permission error """ - path_parts = ('articles', article.pk) + path_parts = ("articles", article.pk) return serve_any_file( - request, - file_to_serve, - public, - hide_name=hide_name, - path_parts=path_parts + request, file_to_serve, public, hide_name=hide_name, path_parts=path_parts ) # MS: I disabled cache control here. When replacing a file, we keep the same # URL, which leads to browsers wrongly serving the old cached version. # @cache_control(max_age=600) -def serve_file_to_browser(file_path, file_to_serve, public=False, - hide_name=False): - """ Stream a file to the browser in a safe way +def serve_file_to_browser(file_path, file_to_serve, public=False, hide_name=False): + """Stream a file to the browser in a safe way :param file_path: the path on disk to the file :param file_to_serve: the core.models.File object to serve @@ -429,22 +462,29 @@ def serve_file_to_browser(file_path, file_to_serve, public=False, if file_to_serve.mime_type in IMAGE_MIMETYPES: response = HttpResponse( - FileWrapper(open(file_path, 'rb'), 8192), + FileWrapper(open(file_path, "rb"), 8192), content_type=file_to_serve.mime_type, ) patch_cache_control(response, max_age=600) else: - response = StreamingHttpResponse(FileWrapper(open(file_path, 'rb'), 8192), content_type=file_to_serve.mime_type) + response = StreamingHttpResponse( + FileWrapper(open(file_path, "rb"), 8192), + content_type=file_to_serve.mime_type, + ) - response['Content-Length'] = os.path.getsize(file_path) + response["Content-Length"] = os.path.getsize(file_path) if public: - response['Content-Disposition'] = 'attachment; filename="{0}"'.format(file_to_serve.public_download_name()) + response["Content-Disposition"] = 'attachment; filename="{0}"'.format( + file_to_serve.public_download_name() + ) elif hide_name: - response['Content-Disposition'] = 'attachment; filename="{0}"'.format( + response["Content-Disposition"] = 'attachment; filename="{0}"'.format( file_to_serve.uuid_filename, ) else: - response['Content-Disposition'] = 'attachment; filename="{0}{1}"'.format(slugify(filename), extension) + response["Content-Disposition"] = 'attachment; filename="{0}{1}"'.format( + slugify(filename), extension + ) return response @@ -458,26 +498,21 @@ def serve_pdf_galley_to_browser(request, file, article): :return: HttpResponse """ file_path = os.path.join( - settings.BASE_DIR, - 'files', - 'articles', - str(article.id), - str(file.uuid_filename) + settings.BASE_DIR, "files", "articles", str(article.id), str(file.uuid_filename) ) try: response = HttpResponse( - FileWrapper(open(file_path, 'rb')), - content_type=file.mime_type + FileWrapper(open(file_path, "rb")), content_type=file.mime_type ) return response except IOError: - messages.add_message(request, messages.ERROR, 'File not found.') + messages.add_message(request, messages.ERROR, "File not found.") raise Http404 def delete_file(article_object, file_object): - """ Deletes a file. Note: the actual file is not deleted, this just removes the association of the file with an + """Deletes a file. Note: the actual file is not deleted, this just removes the association of the file with an article. :param article_object: the article associated with the file @@ -490,8 +525,15 @@ def delete_file(article_object, file_object): article_object.data_figure_files.remove(file_object) -def replace_file(article_to_replace, file_to_replace, new_file, copyedit=None, galley=None, retain_old_label=True): - """ Replaces an existing file with a new record +def replace_file( + article_to_replace, + file_to_replace, + new_file, + copyedit=None, + galley=None, + retain_old_label=True, +): + """Replaces an existing file with a new record :param article_to_replace: the article in which we replace the file :param file_to_replace: the file to be replaces @@ -500,6 +542,7 @@ def replace_file(article_to_replace, file_to_replace, new_file, copyedit=None, g :return: the new file model object """ from core import models + if not copyedit: if file_to_replace in article_to_replace.manuscript_files.all(): article_to_replace.manuscript_files.remove(file_to_replace) @@ -542,34 +585,32 @@ def replace_file(article_to_replace, file_to_replace, new_file, copyedit=None, g def create_file_history_object(file_to_replace): file_history_dict = { - 'mime_type': file_to_replace.mime_type, - 'original_filename': file_to_replace.original_filename, - 'uuid_filename': file_to_replace.uuid_filename, - 'label': file_to_replace.label, - 'description': file_to_replace.description, - 'sequence': file_to_replace.sequence, - 'owner': file_to_replace.owner, - 'privacy': file_to_replace.privacy, - 'history_seq': file_to_replace.next_history_seq() + "mime_type": file_to_replace.mime_type, + "original_filename": file_to_replace.original_filename, + "uuid_filename": file_to_replace.uuid_filename, + "label": file_to_replace.label, + "description": file_to_replace.description, + "sequence": file_to_replace.sequence, + "owner": file_to_replace.owner, + "privacy": file_to_replace.privacy, + "history_seq": file_to_replace.next_history_seq(), } from core import models + new_file = models.FileHistory.objects.create(**file_history_dict) file_to_replace.history.add(new_file) def overwrite_file(uploaded_file, file_to_replace, path_parts=()): - create_file_history_object(file_to_replace) original_filename = str(uploaded_file.name) # N.B. os.path.splitext[1] always returns the final file extension, even in a multi-dotted (.txt.html etc.) input filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) folder_structure = os.path.join( - settings.BASE_DIR, - 'files', - *(str(part) for part in path_parts) + settings.BASE_DIR, "files", *(str(part) for part in path_parts) ) save_file_to_disk(uploaded_file, filename, folder_structure) @@ -587,15 +628,15 @@ def reinstate_historic_file(article, current_file, file_history): create_file_history_object(current_file) file_history_dict = { - 'mime_type': file_history.mime_type, - 'original_filename': file_history.original_filename, - 'uuid_filename': file_history.uuid_filename, - 'label': file_history.label, - 'description': file_history.description, - 'sequence': file_history.sequence, - 'owner': file_history.owner, - 'privacy': file_history.privacy, - 'history_seq': file_history.history_seq + "mime_type": file_history.mime_type, + "original_filename": file_history.original_filename, + "uuid_filename": file_history.uuid_filename, + "label": file_history.label, + "description": file_history.description, + "sequence": file_history.sequence, + "owner": file_history.owner, + "privacy": file_history.privacy, + "history_seq": file_history.history_seq, } for attr, value in file_history_dict.items(): @@ -613,15 +654,22 @@ def serve_journal_cover(request, file_to_serve): permission error """ - file_path = os.path.join(settings.BASE_DIR, 'files', 'journals', str(request.journal.id), - str(file_to_serve.uuid_filename)) + file_path = os.path.join( + settings.BASE_DIR, + "files", + "journals", + str(request.journal.id), + str(file_to_serve.uuid_filename), + ) try: response = serve_file_to_browser(file_path, file_to_serve) return response except IOError: - messages.add_message(request, messages.ERROR, 'File not found. {0}'.format(file_path)) - return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + messages.add_message( + request, messages.ERROR, "File not found. {0}".format(file_path) + ) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) def serve_press_cover(request, file_to_serve): @@ -633,17 +681,23 @@ def serve_press_cover(request, file_to_serve): permission error """ - file_path = os.path.join(settings.BASE_DIR, 'files', 'press', str(file_to_serve.uuid_filename)) + file_path = os.path.join( + settings.BASE_DIR, "files", "press", str(file_to_serve.uuid_filename) + ) try: response = serve_file_to_browser(file_path, file_to_serve) return response except IOError: - messages.add_message(request, messages.ERROR, 'File not found. {0}'.format(file_path)) - return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + messages.add_message( + request, messages.ERROR, "File not found. {0}".format(file_path) + ) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) -def save_file_to_journal(request, file_to_handle, label, description, xslt=False, public=False): +def save_file_to_journal( + request, file_to_handle, label, description, xslt=False, public=False +): original_filename = str(file_to_handle.name) # N.B. os.path.splitext[1] always returns the final file extension, even in a multi-dotted (.txt.html etc.) input @@ -652,13 +706,16 @@ def save_file_to_journal(request, file_to_handle, label, description, xslt=False else: filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'journals', str(request.journal.id)) + folder_structure = os.path.join( + settings.BASE_DIR, "files", "journals", str(request.journal.id) + ) save_file_to_disk(file_to_handle, filename, folder_structure) file_mime = guess_mime(filename) from core import models + new_file = models.File.objects.create( mime_type=file_mime, original_filename=original_filename, @@ -667,33 +724,33 @@ def save_file_to_journal(request, file_to_handle, label, description, xslt=False description=description, owner=request.user, is_galley=False, - privacy="public" if public else "owner" + privacy="public" if public else "owner", ) return new_file -def save_file(request, file_to_handle, label=None, public=False, - path_parts=()): +def save_file(request, file_to_handle, label=None, public=False, path_parts=()): original_filename = str(file_to_handle.name) filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) folder_structure = os.path.join( - settings.BASE_DIR, - 'files', - *(str(part) for part in path_parts), + settings.BASE_DIR, + "files", + *(str(part) for part in path_parts), ) save_file_to_disk(file_to_handle, filename, folder_structure) file_mime = guess_mime(filename) from core import models + new_file = models.File.objects.create( mime_type=file_mime, original_filename=original_filename, uuid_filename=filename, label=label, owner=request.user, - privacy="public" if public else "owner" + privacy="public" if public else "owner", ) return new_file @@ -701,11 +758,13 @@ def save_file(request, file_to_handle, label=None, public=False, def unlink_journal_file(request, file=None, xslt=False): if xslt: - filename = 'journal.xslt' + filename = "journal.xslt" else: filename = file.uuid_filename - full_path = os.path.join(settings.BASE_DIR, 'files', 'journals', str(request.journal.id), filename) + full_path = os.path.join( + settings.BASE_DIR, "files", "journals", str(request.journal.id), filename + ) if os.path.isfile(full_path): os.unlink(full_path) @@ -716,13 +775,14 @@ def save_file_to_press(request, file_to_handle, label, description, public=False # N.B. os.path.splitext[1] always returns the final file extension, even in a multi-dotted (.txt.html etc.) input filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'press') + folder_structure = os.path.join(settings.BASE_DIR, "files", "press") save_file_to_disk(file_to_handle, filename, folder_structure) file_mime = guess_mime(filename) from core import models + new_file = models.File.objects.create( mime_type=file_mime, original_filename=original_filename, @@ -731,7 +791,7 @@ def save_file_to_press(request, file_to_handle, label, description, public=False description=description, owner=request.user, is_galley=False, - privacy="public" if public else "owner" + privacy="public" if public else "owner", ) return new_file @@ -740,14 +800,14 @@ def save_file_to_press(request, file_to_handle, label, description, public=False def save_file_to_temp(file_to_handle): original_filename = str(file_to_handle.name) filename = str(uuid4()) + str(os.path.splitext(original_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'temp') + folder_structure = os.path.join(settings.BASE_DIR, "files", "temp") save_file_to_disk(file_to_handle, filename, folder_structure) return [filename, os.path.join(folder_structure, filename)] def get_temp_file_path_from_name(filename): - return os.path.join(settings.BASE_DIR, 'files', 'temp', filename) + return os.path.join(settings.BASE_DIR, "files", "temp", filename) def file_parents(file): @@ -775,6 +835,7 @@ def file_parents(file): def file_children(file): from core import models + children = models.File.objects.filter(parent=file) return children @@ -787,16 +848,18 @@ def zip_article_files(files, article_folders=False): article name. :return: strings path of the zip file, zip file name """ - file_name = '{0}.zip'.format(uuid4()) + file_name = "{0}.zip".format(uuid4()) # Copy files into a temp dir - _dir = os.path.join(settings.BASE_DIR, 'files/temp', str(uuid4())) + _dir = os.path.join(settings.BASE_DIR, "files/temp", str(uuid4())) os.makedirs(_dir, 0o775) for file in files: if file.article_id: if article_folders: - folder_name = '{id} - {title}'.format(id=file.article_id, title=strip_tags(file.article.title)) + folder_name = "{id} - {title}".format( + id=file.article_id, title=strip_tags(file.article.title) + ) article_dir = os.path.join(_dir, folder_name) if not os.path.exists(article_dir): os.makedirs(article_dir, 0o775) @@ -804,9 +867,9 @@ def zip_article_files(files, article_folders=False): else: shutil.copy(file.self_article_path(), _dir) - zip_path = '{dir}.zip'.format(dir=_dir) + zip_path = "{dir}.zip".format(dir=_dir) - shutil.make_archive(_dir, 'zip', _dir) + shutil.make_archive(_dir, "zip", _dir) shutil.rmtree(_dir) return zip_path, file_name @@ -815,10 +878,14 @@ def serve_temp_file(file_path, file_name): filename, extension = os.path.splitext(file_name) mime_type = guess_mime(file_name) - response = StreamingHttpResponse(FileWrapper(open(file_path, 'rb'), 8192), content_type=mime_type) + response = StreamingHttpResponse( + FileWrapper(open(file_path, "rb"), 8192), content_type=mime_type + ) - response['Content-Length'] = os.path.getsize(file_path) - response['Content-Disposition'] = 'attachment; filename="{0}{1}"'.format(slugify(filename), extension) + response["Content-Length"] = os.path.getsize(file_path) + response["Content-Disposition"] = 'attachment; filename="{0}{1}"'.format( + slugify(filename), extension + ) unlink_temp_file(file_path) @@ -841,46 +908,46 @@ def checksum(file_path): def serve_sitemap_file(path_parts): file_path = os.path.join( settings.BASE_DIR, - 'files', - 'sitemaps', + "files", + "sitemaps", *path_parts, ) return StreamingHttpResponse( FileWrapper( - open(file_path, 'rb'), + open(file_path, "rb"), 8192, ), - content_type='application/xml', + content_type="application/xml", ) def serve_robots_file(journal=None, repository=None): base_path = os.path.join( settings.BASE_DIR, - 'files', - 'robots', + "files", + "robots", ) if journal: file_path = os.path.join( base_path, - 'journal_{}_robots.txt'.format(journal.code), + "journal_{}_robots.txt".format(journal.code), ) elif repository: file_path = os.path.join( base_path, - 'repo_{}_robots.txt'.format(repository.code), + "repo_{}_robots.txt".format(repository.code), ) else: file_path = os.path.join( base_path, - 'robots.txt', + "robots.txt", ) return StreamingHttpResponse( FileWrapper( - open(file_path, 'rb'), + open(file_path, "rb"), 8192, ), - content_type='text/plain', + content_type="text/plain", ) @@ -891,7 +958,9 @@ def copy_preprint_file_to_article(preprint, article, manuscript=True): from core import models if preprint.current_version: - filename = str(uuid4()) + str(os.path.splitext(preprint.current_version.file.original_filename)[1]) + filename = str(uuid4()) + str( + os.path.splitext(preprint.current_version.file.original_filename)[1] + ) path = copy_file_to_folder( preprint.current_version.file.file.path, filename, @@ -902,11 +971,11 @@ def copy_preprint_file_to_article(preprint, article, manuscript=True): mime_type=file_path_mime(path), original_filename=preprint.current_version.file.original_filename, uuid_filename=filename, - label='Manuscript File', - description='File copied from preprint #{}'.format(str(preprint.pk)), + label="Manuscript File", + description="File copied from preprint #{}".format(str(preprint.pk)), owner=article.owner, is_galley=False, - article_id=article.pk + article_id=article.pk, ) if manuscript: @@ -915,7 +984,7 @@ def copy_preprint_file_to_article(preprint, article, manuscript=True): return new_file return None - + def jats_to_text(file_path): text = None with open(file_path, "r") as f: @@ -944,7 +1013,7 @@ def html_to_text(file_path): def pdf_to_text(file_path): text = StringIO() - with open(file_path, 'rb') as file_: + with open(file_path, "rb") as file_: pdf_parser = PDFParser(file_) document = PDFDocument(pdf_parser) resource_manager = PDFResourceManager() diff --git a/src/core/forms/fields.py b/src/core/forms/fields.py index 39562e95b4..1fcb4a6bce 100644 --- a/src/core/forms/fields.py +++ b/src/core/forms/fields.py @@ -21,6 +21,7 @@ def clean(self, data, initial=None): result = single_file_clean(data, initial) return result + class TagitField(forms.CharField): widget = TagitWidget @@ -30,7 +31,6 @@ def to_python(self, value): return tuple(item for item in value.split(",")) except (AttributeError, ValueError): raise ValidationError( - "%s is not a valid value for %s" - "" % (value, self.label) + "%s is not a valid value for %s" "" % (value, self.label) ) return tuple() diff --git a/src/core/forms/forms.py b/src/core/forms/forms.py index 3ce9c45db7..805181cb84 100755 --- a/src/core/forms/forms.py +++ b/src/core/forms/forms.py @@ -36,46 +36,46 @@ class EditKey(forms.Form): def __init__(self, *args, **kwargs): - self.key_type = kwargs.pop('key_type', None) - value = kwargs.pop('value', None) + self.key_type = kwargs.pop("key_type", None) + value = kwargs.pop("value", None) super(EditKey, self).__init__(*args, **kwargs) - if self.key_type == 'rich-text': - self.fields['value'] = JanewayBleachFormField() - elif self.key_type == 'mini-html': - self.fields['value'] = MiniHTMLFormField() - elif self.key_type == 'text': - self.fields['value'].widget = forms.Textarea() - elif self.key_type == 'char': - self.fields['value'].widget = forms.TextInput() - elif self.key_type in {'number', 'integer'}: + if self.key_type == "rich-text": + self.fields["value"] = JanewayBleachFormField() + elif self.key_type == "mini-html": + self.fields["value"] = MiniHTMLFormField() + elif self.key_type == "text": + self.fields["value"].widget = forms.Textarea() + elif self.key_type == "char": + self.fields["value"].widget = forms.TextInput() + elif self.key_type in {"number", "integer"}: # 'integer' is either a bug or used by a plugin - self.fields['value'].widget = forms.TextInput(attrs={'type': 'number'}) - elif self.key_type == 'boolean': - self.fields['value'] = forms.BooleanField(widget=forms.CheckboxInput) - elif self.key_type == 'file' or self.key_type == 'journalthumb': - self.fields['value'].widget = forms.FileInput() - elif self.key_type == 'json': - self.fields['value'].widget = forms.Textarea() + self.fields["value"].widget = forms.TextInput(attrs={"type": "number"}) + elif self.key_type == "boolean": + self.fields["value"] = forms.BooleanField(widget=forms.CheckboxInput) + elif self.key_type == "file" or self.key_type == "journalthumb": + self.fields["value"].widget = forms.FileInput() + elif self.key_type == "json": + self.fields["value"].widget = forms.Textarea() else: - self.fields['value'].widget.attrs['size'] = '100%' + self.fields["value"].widget.attrs["size"] = "100%" - self.fields['value'].initial = value - self.fields['value'].required = False - self.fields['value'].label = '' + self.fields["value"].initial = value + self.fields["value"].required = False + self.fields["value"].label = "" - value = forms.CharField(label='') + value = forms.CharField(label="") def clean(self): cleaned_data = self.cleaned_data - if self.key_type == 'json': + if self.key_type == "json": try: - json.loads(cleaned_data.get('value')) + json.loads(cleaned_data.get("value")) except json.JSONDecodeError as e: self.add_error( - 'value', - f'JSON not valid: {e}', + "value", + f"JSON not valid: {e}", ) return cleaned_data @@ -83,98 +83,113 @@ def clean(self): class JournalContactForm(JanewayTranslationModelForm): def __init__(self, *args, **kwargs): - next_sequence = kwargs.pop('next_sequence', None) + next_sequence = kwargs.pop("next_sequence", None) super(JournalContactForm, self).__init__(*args, **kwargs) if next_sequence: - self.fields['sequence'].initial = next_sequence + self.fields["sequence"].initial = next_sequence class Meta: model = models.Contacts - fields = ('name', 'email', 'role', 'sequence',) - exclude = ('content_type', 'object_id',) + fields = ( + "name", + "email", + "role", + "sequence", + ) + exclude = ( + "content_type", + "object_id", + ) class EditorialGroupForm(JanewayTranslationModelForm): - def __init__(self, *args, **kwargs): - next_sequence = kwargs.pop('next_sequence', None) + next_sequence = kwargs.pop("next_sequence", None) super(EditorialGroupForm, self).__init__(*args, **kwargs) if next_sequence: - self.fields['sequence'].initial = next_sequence + self.fields["sequence"].initial = next_sequence class Meta: model = models.EditorialGroup - fields = ('name', 'description', 'sequence', 'display_profile_images') - exclude = ('journal', 'press') + fields = ("name", "description", "sequence", "display_profile_images") + exclude = ("journal", "press") class PasswordResetForm(forms.Form): - - password_1 = forms.CharField(widget=forms.PasswordInput, label=_('Password')) - password_2 = forms.CharField(widget=forms.PasswordInput, label=_('Repeat Password')) + password_1 = forms.CharField(widget=forms.PasswordInput, label=_("Password")) + password_2 = forms.CharField(widget=forms.PasswordInput, label=_("Repeat Password")) def clean_password_2(self): password_1 = self.cleaned_data.get("password_1") password_2 = self.cleaned_data.get("password_2") if password_1 and password_2 and password_1 != password_2: raise forms.ValidationError( - 'Your passwords do not match.', - code='password_mismatch', + "Your passwords do not match.", + code="password_mismatch", ) return password_2 class GetResetTokenForm(forms.Form): - """ A form that validates password reset email addresses""" + """A form that validates password reset email addresses""" email_address = forms.EmailField( - required=True, + required=True, label=_("Email"), widget=forms.EmailInput(attrs={"placeholder": "janeway@example.com"}), ) - + class RegistrationForm(forms.ModelForm, CaptchaForm): - """ A form that creates a user, with no privileges, + """A form that creates a user, with no privileges, from the given username and password.""" - password_1 = forms.CharField(widget=forms.PasswordInput, label=_('Password')) - password_2 = forms.CharField(widget=forms.PasswordInput, label=_('Repeat Password')) + password_1 = forms.CharField(widget=forms.PasswordInput, label=_("Password")) + password_2 = forms.CharField(widget=forms.PasswordInput, label=_("Repeat Password")) register_as_reader = forms.BooleanField( - label='Register for Article Notifications', - help_text=_('Check this box if you would like to receive notifications of new articles published in this journal'), + label="Register for Article Notifications", + help_text=_( + "Check this box if you would like to receive notifications of new articles published in this journal" + ), required=False, ) class Meta: model = models.Account - fields = ('email', 'salutation', 'first_name', 'middle_name', - 'last_name', 'department', 'institution', 'country', 'orcid',) - widgets = {'orcid': forms.HiddenInput() } + fields = ( + "email", + "salutation", + "first_name", + "middle_name", + "last_name", + "department", + "institution", + "country", + "orcid", + ) + widgets = {"orcid": forms.HiddenInput()} def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(RegistrationForm, self).__init__(*args, **kwargs) if not self.journal: - self.fields.pop('register_as_reader') + self.fields.pop("register_as_reader") elif self.journal: send_reader_notifications = setting_handler.get_setting( - 'notifications', - 'send_reader_notifications', - self.journal + "notifications", "send_reader_notifications", self.journal ).value if not send_reader_notifications: - self.fields.pop('register_as_reader') + self.fields.pop("register_as_reader") def clean_password_2(self): password_1 = self.cleaned_data.get("password_1") password_2 = self.cleaned_data.get("password_2") if password_1 and password_2 and password_1 != password_2: raise forms.ValidationError( - 'Your passwords do not match.', - code='password_mismatch', + "Your passwords do not match.", + code="password_mismatch", ) return password_2 @@ -188,7 +203,7 @@ def save(self, commit=True): if commit: user.save() - if self.cleaned_data.get('register_as_reader') and self.journal: + if self.cleaned_data.get("register_as_reader") and self.journal: user.add_account_role( role_slug="reader", journal=self.journal, @@ -198,26 +213,37 @@ def save(self, commit=True): class EditAccountForm(forms.ModelForm): - """ A form that creates a user, with no privileges, from the given username and password.""" + """A form that creates a user, with no privileges, from the given username and password.""" interests = forms.CharField(required=False) class Meta: model = models.Account - exclude = ('email', 'username', 'activation_code', 'email_sent', - 'date_confirmed', 'confirmation_code', 'is_active', - 'is_staff', 'is_admin', 'date_joined', 'password', - 'is_superuser', 'enable_digest') + exclude = ( + "email", + "username", + "activation_code", + "email_sent", + "date_confirmed", + "confirmation_code", + "is_active", + "is_staff", + "is_admin", + "date_joined", + "password", + "is_superuser", + "enable_digest", + ) widgets = { - 'biography': TinyMCE(), - 'signature': TinyMCE(), + "biography": TinyMCE(), + "signature": TinyMCE(), } def save(self, commit=True): user = super(EditAccountForm, self).save(commit=False) user.clean() - posted_interests = self.cleaned_data['interests'].split(',') + posted_interests = self.cleaned_data["interests"].split(",") for interest in posted_interests: new_interest, c = models.Interest.objects.get_or_create(name=interest) user.interest.add(new_interest) @@ -235,29 +261,32 @@ def save(self, commit=True): class AdminUserForm(forms.ModelForm): - class Meta: model = models.Account - fields = ('email', 'is_active', 'is_staff', 'is_admin', 'is_superuser') + fields = ("email", "is_active", "is_staff", "is_admin", "is_superuser") def __init__(self, *args, **kwargs): - active = kwargs.pop('active', None) - request = kwargs.pop('request', None) + active = kwargs.pop("active", None) + request = kwargs.pop("request", None) super(AdminUserForm, self).__init__(*args, **kwargs) - if not kwargs.get('instance', None): - self.fields['is_active'].initial = True + if not kwargs.get("instance", None): + self.fields["is_active"].initial = True - if active == 'add': - self.fields['password_1'] = forms.CharField(widget=forms.PasswordInput, label="Password") - self.fields['password_2'] = forms.CharField(widget=forms.PasswordInput, label="Repeat Password") + if active == "add": + self.fields["password_1"] = forms.CharField( + widget=forms.PasswordInput, label="Password" + ) + self.fields["password_2"] = forms.CharField( + widget=forms.PasswordInput, label="Repeat Password" + ) if request and not request.user.is_admin: - self.fields.pop('is_staff', None) - self.fields.pop('is_admin', None) + self.fields.pop("is_staff", None) + self.fields.pop("is_admin", None) if request and not request.user.is_superuser: - self.fields.pop('is_superuser') + self.fields.pop("is_superuser") def clean_password_2(self): password_1 = self.cleaned_data.get("password_1") @@ -265,14 +294,14 @@ def clean_password_2(self): if password_1 and password_2 and password_1 != password_2: raise forms.ValidationError( - 'Your passwords do not match.', - code='password_mismatch', + "Your passwords do not match.", + code="password_mismatch", ) if password_2 and not len(password_2) >= 12: raise forms.ValidationError( - 'Your password is too short, it should be 12 characters or greater in length.', - code='password_to_short', + "Your password is too short, it should be 12 characters or greater in length.", + code="password_to_short", ) return password_2 @@ -280,7 +309,7 @@ def clean_password_2(self): def save(self, commit=True): user = super(AdminUserForm, self).save(commit=False) - if self.cleaned_data.get('password_1'): + if self.cleaned_data.get("password_1"): user.set_password(self.cleaned_data["password_1"]) user.save() @@ -291,100 +320,119 @@ def save(self, commit=True): class GeneratedPluginSettingForm(forms.Form): - def __init__(self, *args, **kwargs): - settings = kwargs.pop('settings', None) + settings = kwargs.pop("settings", None) super(GeneratedPluginSettingForm, self).__init__(*args, **kwargs) for field in settings: - - object = field['object'] - if field['types'] == 'char': - self.fields[field['name']] = forms.CharField(widget=forms.TextInput(), required=False) - elif field['types'] == 'rich-text': - self.fields[field['name']] = JanewayBleachFormField( + object = field["object"] + if field["types"] == "char": + self.fields[field["name"]] = forms.CharField( + widget=forms.TextInput(), required=False + ) + elif field["types"] == "rich-text": + self.fields[field["name"]] = JanewayBleachFormField( required=False, - ) - elif field['types'] == 'mini-html': - self.fields[field['name']] = MiniHTMLFormField( + ) + elif field["types"] == "mini-html": + self.fields[field["name"]] = MiniHTMLFormField( required=False, ) - elif field['types'] in {'text', 'Text'}: + elif field["types"] in {"text", "Text"}: # Keeping Text because a plugin may use it - self.fields[field['name']] = forms.CharField( + self.fields[field["name"]] = forms.CharField( widget=forms.Textarea, required=False, ) - elif field['types'] == 'json': - self.fields[field['name']] = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, - choices=field['choices'], - required=False) - elif field['types'] == 'number': - self.fields[field['name']] = forms.CharField(widget=forms.TextInput(attrs={'type': 'number'})) - elif field['types'] == 'select': - self.fields[field['name']] = forms.CharField(widget=forms.Select(choices=field['choices'])) - elif field['types'] == 'date': - self.fields[field['name']] = forms.CharField( - widget=forms.DateInput(attrs={'class': 'datepicker'})) - elif field['types'] == 'boolean': - self.fields[field['name']] = forms.BooleanField( - widget=forms.CheckboxInput(attrs={'is_checkbox': True}), - required=False) - - self.fields[field['name']].initial = object.processed_value - self.fields[field['name']].help_text = object.setting.description + elif field["types"] == "json": + self.fields[field["name"]] = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + choices=field["choices"], + required=False, + ) + elif field["types"] == "number": + self.fields[field["name"]] = forms.CharField( + widget=forms.TextInput(attrs={"type": "number"}) + ) + elif field["types"] == "select": + self.fields[field["name"]] = forms.CharField( + widget=forms.Select(choices=field["choices"]) + ) + elif field["types"] == "date": + self.fields[field["name"]] = forms.CharField( + widget=forms.DateInput(attrs={"class": "datepicker"}) + ) + elif field["types"] == "boolean": + self.fields[field["name"]] = forms.BooleanField( + widget=forms.CheckboxInput(attrs={"is_checkbox": True}), + required=False, + ) + + self.fields[field["name"]].initial = object.processed_value + self.fields[field["name"]].help_text = object.setting.description def save(self, journal, plugin, commit=True): for setting_name, setting_value in self.cleaned_data.items(): - setting_handler.save_plugin_setting(plugin, setting_name, setting_value, journal) + setting_handler.save_plugin_setting( + plugin, setting_name, setting_value, journal + ) class GeneratedSettingForm(forms.Form): - def __init__(self, *args, **kwargs): - settings = kwargs.pop('settings', None) + settings = kwargs.pop("settings", None) super(GeneratedSettingForm, self).__init__(*args, **kwargs) self.translatable_field_names = [] for field in settings: - object = field['object'] + object = field["object"] - if object.setting.types == 'char': - self.fields[field['name']] = forms.CharField(widget=forms.TextInput(), required=False) - elif object.setting.types == 'rich-text': - self.fields[field['name']] = JanewayBleachFormField( + if object.setting.types == "char": + self.fields[field["name"]] = forms.CharField( + widget=forms.TextInput(), required=False + ) + elif object.setting.types == "rich-text": + self.fields[field["name"]] = JanewayBleachFormField( required=False, ) - elif object.setting.types == 'mini-html': - self.fields[field['name']] = MiniHTMLFormField( + elif object.setting.types == "mini-html": + self.fields[field["name"]] = MiniHTMLFormField( required=False, ) - elif object.setting.types == 'text': - self.fields[field['name']] = forms.CharField( + elif object.setting.types == "text": + self.fields[field["name"]] = forms.CharField( widget=forms.Textarea, required=False, ) - elif object.setting.types == 'json': - self.fields[field['name']] = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, - choices=field['choices'], - required=False) - elif object.setting.types == 'number': - self.fields[field['name']] = forms.CharField(widget=forms.TextInput(attrs={'type': 'number'})) - elif object.setting.types == 'select': - self.fields[field['name']] = forms.CharField(widget=forms.Select(choices=field['choices'])) - elif object.setting.types == 'date': - self.fields[field['name']] = forms.CharField( - widget=forms.DateInput(attrs={'class': 'datepicker'})) - elif object.setting.types == 'boolean': - self.fields[field['name']] = forms.BooleanField( - widget=forms.CheckboxInput(attrs={'is_checkbox': True}), - required=False) + elif object.setting.types == "json": + self.fields[field["name"]] = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + choices=field["choices"], + required=False, + ) + elif object.setting.types == "number": + self.fields[field["name"]] = forms.CharField( + widget=forms.TextInput(attrs={"type": "number"}) + ) + elif object.setting.types == "select": + self.fields[field["name"]] = forms.CharField( + widget=forms.Select(choices=field["choices"]) + ) + elif object.setting.types == "date": + self.fields[field["name"]] = forms.CharField( + widget=forms.DateInput(attrs={"class": "datepicker"}) + ) + elif object.setting.types == "boolean": + self.fields[field["name"]] = forms.BooleanField( + widget=forms.CheckboxInput(attrs={"is_checkbox": True}), + required=False, + ) if object.setting.is_translatable: self.translatable_field_names.append(object.setting.name) - self.fields[field['name']].label = object.setting.pretty_name - self.fields[field['name']].initial = object.processed_value - self.fields[field['name']].help_text = object.setting.description + self.fields[field["name"]].label = object.setting.pretty_name + self.fields[field["name"]].initial = object.processed_value + self.fields[field["name"]].help_text = object.setting.description def save(self, journal, group, commit=True): for setting_name, setting_value in self.cleaned_data.items(): @@ -395,11 +443,11 @@ class JournalAttributeForm(JanewayTranslationModelForm, KeywordModelForm): class Meta: model = journal_models.Journal fields = ( - 'contact_info', - 'is_remote', - 'remote_view_url', - 'remote_submit_url', - 'hide_from_press', + "contact_info", + "is_remote", + "remote_view_url", + "remote_submit_url", + "hide_from_press", ) @@ -409,35 +457,34 @@ class JournalImageForm(forms.ModelForm): class Meta: model = journal_models.Journal fields = ( - 'header_image', 'default_cover_image', - 'default_large_image', 'favicon', 'press_image_override', - 'default_profile_image', + "header_image", + "default_cover_image", + "default_large_image", + "favicon", + "press_image_override", + "default_profile_image", ) class JournalStylingForm(forms.ModelForm): class Meta: model = journal_models.Journal - fields = ( - 'full_width_navbar', - ) + fields = ("full_width_navbar",) class JournalSubmissionForm(forms.ModelForm): class Meta: model = journal_models.Journal - fields = ( - 'enable_correspondence_authors', - ) + fields = ("enable_correspondence_authors",) class JournalArticleForm(forms.ModelForm): class Meta: model = journal_models.Journal fields = ( - 'view_pdf_button', - 'disable_metrics_display', - 'disable_html_downloads', + "view_pdf_button", + "disable_metrics_display", + "disable_html_downloads", ) @@ -448,21 +495,28 @@ class PressJournalAttrForm(KeywordModelForm, JanewayTranslationModelForm): class Meta: model = journal_models.Journal fields = ( - 'contact_info', 'header_image', 'default_cover_image', - 'default_large_image', 'favicon', 'is_remote', 'is_conference', - 'remote_view_url', 'remote_submit_url', 'hide_from_press', - 'disable_metrics_display', + "contact_info", + "header_image", + "default_cover_image", + "default_large_image", + "favicon", + "is_remote", + "is_conference", + "remote_view_url", + "remote_submit_url", + "hide_from_press", + "disable_metrics_display", ) class NotificationForm(forms.ModelForm): class Meta: model = journal_models.Notifications - exclude = ('journal',) + exclude = ("journal",) widgets = { - 'active': forms.CheckboxInput( + "active": forms.CheckboxInput( attrs={ - 'is_checkbox': True, + "is_checkbox": True, } ), } @@ -471,43 +525,57 @@ class Meta: class ArticleMetaImageForm(forms.ModelForm): class Meta: model = submission_models.Article - fields = ('meta_image',) + fields = ("meta_image",) class SectionForm(JanewayTranslationModelForm): class Meta: model = submission_models.Section fields = [ - 'name', 'plural', 'number_of_reviewers', - 'is_filterable', 'sequence', 'section_editors', - 'editors', 'public_submissions', 'indexing', - 'auto_assign_editors', + "name", + "plural", + "number_of_reviewers", + "is_filterable", + "sequence", + "section_editors", + "editors", + "public_submissions", + "indexing", + "auto_assign_editors", ] def __init__(self, *args, **kwargs): - request = kwargs.pop('request', None) + request = kwargs.pop("request", None) super(SectionForm, self).__init__(*args, **kwargs) if request: - self.fields['section_editors'].queryset = request.journal.users_with_role( - 'section-editor', + self.fields["section_editors"].queryset = request.journal.users_with_role( + "section-editor", ) - self.fields['section_editors'].required = False - self.fields['editors'].queryset = request.journal.users_with_role('editor') - self.fields['editors'].required = False + self.fields["section_editors"].required = False + self.fields["editors"].queryset = request.journal.users_with_role("editor") + self.fields["editors"].required = False class QuickUserForm(forms.ModelForm): class Meta: model = models.Account - fields = ('email', 'salutation', 'first_name', 'last_name', 'institution',) + fields = ( + "email", + "salutation", + "first_name", + "last_name", + "institution", + ) class LoginForm(CaptchaForm): user_name = forms.CharField(max_length=255, label="Email") - user_pass = forms.CharField(max_length=255, label="Password", widget=forms.PasswordInput) + user_pass = forms.CharField( + max_length=255, label="Password", widget=forms.PasswordInput + ) def __init__(self, *args, **kwargs): - bad_logins = kwargs.pop('bad_logins', 0) + bad_logins = kwargs.pop("bad_logins", 0) super(LoginForm, self).__init__(*args, **kwargs) if bad_logins: logger.warning( @@ -515,7 +583,9 @@ def __init__(self, *args, **kwargs): "" % (self.fields["user_name"], bad_logins), ) if bad_logins <= 3: - self.fields['captcha'] = forms.CharField(widget=forms.HiddenInput(), required=False) + self.fields["captcha"] = forms.CharField( + widget=forms.HiddenInput(), required=False + ) class FileUploadForm(forms.Form): @@ -524,8 +594,8 @@ class FileUploadForm(forms.Form): def __init__(self, *args, extensions=None, mimetypes=None, **kwargs): super().__init__(*args, **kwargs) validator = validators.FileTypeValidator( - extensions=extensions, - mimetypes=mimetypes, + extensions=extensions, + mimetypes=mimetypes, ) self.fields["file"].validators.append(validator) @@ -533,14 +603,13 @@ def __init__(self, *args, extensions=None, mimetypes=None, **kwargs): class UserCreationFormExtended(UserCreationForm): def __init__(self, *args, **kwargs): super(UserCreationFormExtended, self).__init__(*args, **kwargs) - self.fields['email'] = forms.EmailField( + self.fields["email"] = forms.EmailField( label=_("E-mail"), max_length=75, ) class XSLFileForm(forms.ModelForm): - class Meta: model = models.XSLFile exclude = ["date_uploaded", "journal", "original_filename"] @@ -559,19 +628,18 @@ def save(self, commit=True): class AccessRequestForm(forms.ModelForm): - class Meta: model = models.AccessRequest - fields = ('text',) + fields = ("text",) labels = { - 'text': 'Supporting Information', + "text": "Supporting Information", } def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) - self.repository = kwargs.pop('repository', None) - self.user = kwargs.pop('user') - self.role = kwargs.pop('role') + self.journal = kwargs.pop("journal", None) + self.repository = kwargs.pop("repository", None) + self.user = kwargs.pop("user") + self.role = kwargs.pop("role") super(AccessRequestForm, self).__init__(*args, **kwargs) def save(self, commit=True): @@ -588,34 +656,31 @@ def save(self, commit=True): class CBVFacetForm(forms.Form): - def __init__(self, *args, **kwargs): # This form populates the facets that users can filter results on. # The facets dynamically change based on the queryset of results, # so users only see filter options that will have an effect on the # current results. - self.id = 'facet_form' - self.queryset = kwargs.pop('queryset') - self.facets = kwargs.pop('facets') + self.id = "facet_form" + self.queryset = kwargs.pop("queryset") + self.facets = kwargs.pop("facets") self.fields = {} super().__init__(*args, **kwargs) for facet_key, facet in self.facets.items(): - - if facet['type'] == 'foreign_key': - + if facet["type"] == "foreign_key": # Note: This retrieval is written to work even for sqlite3. # It might be rewritten differently if sqlite3 support isn't needed. column = self.queryset.values_list(facet_key, flat=True) values_list = list(filter(bool, column)) - choice_queryset = facet['model'].objects.filter(pk__in=values_list) + choice_queryset = facet["model"].objects.filter(pk__in=values_list) choices = [] for each in choice_queryset: label = getattr(each, facet["choice_label_field"]) count = self.queryset.filter(Q((facet_key, each.pk))).count() - label_with_count = f'{label} ({count})' + label_with_count = f"{label} ({count})" choices.append((each.pk, label_with_count)) choices = sorted(choices, key=lambda x: x[1]) @@ -625,13 +690,13 @@ def __init__(self, *args, **kwargs): required=False, ) - elif facet['type'] == 'charfield_with_choices': + elif facet["type"] == "charfield_with_choices": # Note: This retrieval is written to work even for sqlite3. # It might be rewritten differently if sqlite3 support isn't needed. column = [] values_list = [] - lookup_parts = facet_key.split('.') + lookup_parts = facet_key.split(".") for obj in self.queryset: for part in lookup_parts: if obj: @@ -643,16 +708,16 @@ def __init__(self, *args, **kwargs): if result != None: values_list.append(result) - elif result == None and 'default' in facet: - values_list.append(facet['default']) + elif result == None and "default" in facet: + values_list.append(facet["default"]) unique_values = set(values_list) choices = [] - model_choice_dict = dict(facet['model_choices']) + model_choice_dict = dict(facet["model_choices"]) for value in unique_values: label = model_choice_dict.get(value, value) count = values_list.count(value) - label_with_count = f'{label} ({count})' + label_with_count = f"{label} ({count})" choices.append((value, label_with_count)) self.fields[facet_key] = forms.ChoiceField( widget=forms.widgets.CheckboxSelectMultiple, @@ -660,64 +725,55 @@ def __init__(self, *args, **kwargs): required=False, ) - elif facet['type'] == 'date_time': + elif facet["type"] == "date_time": self.fields[facet_key] = forms.DateTimeField( required=False, - widget=forms.DateTimeInput( - attrs={'type': 'datetime-local'} - ), + widget=forms.DateTimeInput(attrs={"type": "datetime-local"}), ) - elif facet['type'] == 'date': + elif facet["type"] == "date": self.fields[facet_key] = forms.DateField( required=False, - widget=forms.DateInput( - attrs={'type': 'date'} - ), + widget=forms.DateInput(attrs={"type": "date"}), ) - elif facet['type'] == 'integer': + elif facet["type"] == "integer": self.fields[facet_key] = forms.IntegerField( required=False, ) - elif facet['type'] == 'search': + elif facet["type"] == "search": self.fields[facet_key] = forms.CharField( required=False, - widget=forms.TextInput( - attrs={'type': 'search'} - ), + widget=forms.TextInput(attrs={"type": "search"}), ) - elif facet['type'] == 'boolean': + elif facet["type"] == "boolean": self.fields[facet_key] = forms.TypedChoiceField( widget=forms.widgets.RadioSelect, choices=[ - ('', facet.get('all_label', 'All')), - (1, facet.get('true_label', 'Yes')), - (0, facet.get('false_label', 'No')), + ("", facet.get("all_label", "All")), + (1, facet.get("true_label", "Yes")), + (0, facet.get("false_label", "No")), ], required=False, coerce=int, ) - self.fields[facet_key].label = facet['field_label'] + self.fields[facet_key].label = facet["field_label"] def order_by(self, queryset, facet, fks): - order_by = facet.get('order_by') - if order_by != 'facet_count' and order_by in facet['model']._meta.get_fields(): + order_by = facet.get("order_by") + if order_by != "facet_count" and order_by in facet["model"]._meta.get_fields(): queryset = queryset.order_by(order_by) - elif order_by == 'facet_count': + elif order_by == "facet_count": sorted_fk_tuples = sorted( [(fk, fks.count(fk)) for fk in fks], - key=lambda x:x[1], + key=lambda x: x[1], reverse=True, ) sorted_fks = [tup[0] for tup in sorted_fk_tuples] - queryset = sorted( - queryset, - key=lambda x: sorted_fks.index(x.pk) - ) + queryset = sorted(queryset, key=lambda x: sorted_fks.index(x.pk)) return queryset @@ -737,9 +793,9 @@ class ConfirmableForm(forms.Form): if there are errrors, see ConfirmableIfErrorsForm. """ - CONFIRMABLE_BUTTON_NAME = 'confirmable' - CONFIRMED_BUTTON_NAME = 'confirmed' - QUESTION = _('Are you sure?') + CONFIRMABLE_BUTTON_NAME = "confirmable" + CONFIRMED_BUTTON_NAME = "confirmed" + QUESTION = _("Are you sure?") def __init__(self, *args, **kwargs): self.modal = None @@ -752,12 +808,11 @@ def is_valid(self, *args, **kwargs): return parent_return def create_modal(self): - self.modal = { - 'id': 'confirm_modal', - 'confirmed_button_name': self.CONFIRMED_BUTTON_NAME, - 'question': self.QUESTION, - 'potential_errors': self.check_for_potential_errors(), + "id": "confirm_modal", + "confirmed_button_name": self.CONFIRMED_BUTTON_NAME, + "question": self.QUESTION, + "potential_errors": self.check_for_potential_errors(), } def check_for_potential_errors(self): @@ -768,11 +823,13 @@ def check_for_inactive_account(self, account): try: account = models.Account.objects.get(id=account) except models.Account.DoesNotExist: - return 'Could not check account status' + return "Could not check account status" if not account.is_active: - return _('The account belonging to %(email)s has not yet been activated, ' \ - 'so the recipient of this assignment may not be able ' \ - 'to log in and view it.') % {'email': account.email} + return _( + "The account belonging to %(email)s has not yet been activated, " + "so the recipient of this assignment may not be able " + "to log in and view it." + ) % {"email": account.email} def is_confirmed(self): return self.CONFIRMED_BUTTON_NAME in self.data @@ -811,22 +868,22 @@ class EmailForm(forms.Form): attachments = MultipleFileField(required=False) def clean_cc(self): - cc = self.cleaned_data['cc'] + cc = self.cleaned_data["cc"] return self.email_sequence_cleaner("cc", cc) def clean_bcc(self): - cc = self.cleaned_data['bcc'] + cc = self.cleaned_data["bcc"] return self.email_sequence_cleaner("bcc", cc) def email_sequence_cleaner(self, field, email_seq): - if not email_seq or email_seq == '': + if not email_seq or email_seq == "": return tuple() for address in email_seq: try: validate_email(address) except ValidationError: - self.add_error(field, 'Invalid email address ({}).'.format(address)) + self.add_error(field, "Invalid email address ({}).".format(address)) return email_seq @@ -835,22 +892,22 @@ def as_dataclass(self): class FullEmailForm(EmailForm): - """ An email form that includes the To field - """ + """An email form that includes the To field""" + to = TagitField( required=True, max_length=10000, ) - field_order = ['to', 'cc', 'bcc', 'subject', 'body', 'attachments'] + field_order = ["to", "cc", "bcc", "subject", "body", "attachments"] def clean_to(self): - to = self.cleaned_data['to'] + to = self.cleaned_data["to"] return self.email_sequence_cleaner("to", to) class SettingEmailForm(EmailForm): - """ An Email form that populates initial data using Janeway email settings + """An Email form that populates initial data using Janeway email settings During initialization, the email and subject settings are retrieved, matching the given setting_name @@ -859,6 +916,7 @@ class SettingEmailForm(EmailForm): :param request: The instance of this HttpRequest :param journal: (Optional) an instance of journal.models.Journal """ + def __init__(self, *args, **kwargs): setting_name = kwargs.pop("setting_name") email_context = kwargs.pop("email_context", {}) @@ -880,14 +938,13 @@ def __init__(self, *args, **kwargs): class FullSettingEmailForm(SettingEmailForm, FullEmailForm): - """ A setting-based email form that includes the To field - """ + """A setting-based email form that includes the To field""" + pass class SimpleTinyMCEForm(forms.Form): - """ A one-field form for populating a TinyMCE textarea - """ + """A one-field form for populating a TinyMCE textarea""" def __init__(self, field_name, *args, **kwargs): super().__init__(*args, **kwargs) @@ -895,7 +952,6 @@ def __init__(self, field_name, *args, **kwargs): class AccountRoleForm(forms.ModelForm): - class Meta: model = models.AccountRole - fields = '__all__' + fields = "__all__" diff --git a/src/core/forms/widgets.py b/src/core/forms/widgets.py index 09b3710a44..3527d10834 100644 --- a/src/core/forms/widgets.py +++ b/src/core/forms/widgets.py @@ -11,12 +11,13 @@ def __init__(self, attrs=None, *args, **kwargs): super().__init__(attrs, *args, **kwargs) class Media: - css = {"all": ( - "https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css", - )} + css = { + "all": ( + "https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css", + ) + } js = ( "common/js/jq-ui.min.js", "common/js/tagit.js", "common/js/tagit-widget.js", ) - diff --git a/src/core/homepage_elements/about/forms.py b/src/core/homepage_elements/about/forms.py index 74ff2ac90d..45bdc5c80c 100644 --- a/src/core/homepage_elements/about/forms.py +++ b/src/core/homepage_elements/about/forms.py @@ -11,24 +11,24 @@ class AboutForm(forms.Form): ) description = forms.CharField( widget=TinyMCE, - help_text='A general description of the journal.', + help_text="A general description of the journal.", ) def save(self, journal, commit=True): - title = self.cleaned_data.get('title') - description = self.cleaned_data.get('description') + title = self.cleaned_data.get("title") + description = self.cleaned_data.get("description") if commit: setting_handler.save_plugin_setting( plugin_settings.get_self(), - 'about_title', + "about_title", title, journal, ) setting_handler.save_setting( - 'general', - 'journal_description', + "general", + "journal_description", journal, description, ) diff --git a/src/core/homepage_elements/about/hooks.py b/src/core/homepage_elements/about/hooks.py index 8521acc935..d5a2df33f3 100755 --- a/src/core/homepage_elements/about/hooks.py +++ b/src/core/homepage_elements/about/hooks.py @@ -10,21 +10,23 @@ def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter(name='About').exists(): - + if ( + homepage_elements is not None + and homepage_elements.filter(name="About").exists() + ): try: title = get_plugin_setting( plugin_settings.get_self(), - 'about_title', + "about_title", request.journal, ) - title_value = title.value if title.value else '' + title_value = title.value if title.value else "" except AttributeError: - title_value = _('About this Journal') + title_value = _("About this Journal") return { - 'about_content': request.journal.description, - 'title_value': title_value, + "about_content": request.journal.description, + "title_value": title_value, } else: return {} diff --git a/src/core/homepage_elements/about/plugin_settings.py b/src/core/homepage_elements/about/plugin_settings.py index cb6d0317f3..b0815471ba 100755 --- a/src/core/homepage_elements/about/plugin_settings.py +++ b/src/core/homepage_elements/about/plugin_settings.py @@ -11,10 +11,10 @@ logger = get_logger(__name__) -PLUGIN_NAME = 'About' -DESCRIPTION = 'This is a homepage element that renders About this Journal section.' -AUTHOR = 'Martin Paul Eve & Andy Byers' -VERSION = '1.1' +PLUGIN_NAME = "About" +DESCRIPTION = "This is a homepage element that renders About this Journal section." +AUTHOR = "Martin Paul Eve & Andy Byers" +VERSION = "1.1" def get_self(): @@ -34,11 +34,11 @@ def install(): plugin, c = models.Plugin.objects.get_or_create( name=PLUGIN_NAME, defaults={ - 'homepage_element': True, - 'enabled': True, - 'version': VERSION, - 'display_name': 'About', - } + "homepage_element": True, + "enabled": True, + "version": VERSION, + "display_name": "About", + }, ) if not c: @@ -46,29 +46,29 @@ def install(): plugin.save() if c: - logger.debug('Plugin installed.') + logger.debug("Plugin installed.") - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=plugin.name) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=plugin.name) setting_group, c = core_models.SettingGroup.objects.get_or_create( name=plugin_group_name, ) setting, c = core_models.Setting.objects.get_or_create( - name='about_title', + name="about_title", group=setting_group, defaults={ - 'pretty_name': 'About Block Title', - 'types': 'text', - 'description': 'Sets the title of the About Block', - 'is_translatable': True, - } + "pretty_name": "About Block Title", + "types": "text", + "description": "Sets the title of the About Block", + "is_translatable": True, + }, ) if c: - logger.debug('Setting created') + logger.debug("Setting created") setting_handler.get_or_create_default_setting( setting, - default_value='About this Journal', + default_value="About this Journal", ) # check whether this homepage element has already @@ -79,11 +79,12 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='journal_description', - template_path='journal/homepage_elements/about.html', + configure_url="journal_description", + template_path="journal/homepage_elements/about.html", content_type=content_type, object_id=journal.pk, - has_config=True) + has_config=True, + ) element.save() @@ -91,12 +92,11 @@ def install(): def hook_registry(): try: return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.about.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.about.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } - } except OperationalError: # if we get here the database hasn't yet been created diff --git a/src/core/homepage_elements/about/urls.py b/src/core/homepage_elements/about/urls.py index 9973c4789a..2fd8a441b2 100755 --- a/src/core/homepage_elements/about/urls.py +++ b/src/core/homepage_elements/about/urls.py @@ -8,5 +8,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.journal_description, name='journal_description'), + re_path(r"^$", views.journal_description, name="journal_description"), ] diff --git a/src/core/homepage_elements/about/views.py b/src/core/homepage_elements/about/views.py index d05d3e74ec..619b4b5039 100755 --- a/src/core/homepage_elements/about/views.py +++ b/src/core/homepage_elements/about/views.py @@ -14,22 +14,23 @@ @editor_user_required def journal_description(request): - title = get_plugin_setting( plugin_settings.get_self(), - 'about_title', + "about_title", request.journal, create=True, ) - description = request.journal.get_setting('general', 'journal_description') + description = request.journal.get_setting("general", "journal_description") form = forms.AboutForm( initial={ - 'title': title.value if title else 'About {name}'.format( + "title": title.value + if title + else "About {name}".format( name=request.journal.name, ), - 'description': description, + "description": description, } ) @@ -37,12 +38,12 @@ def journal_description(request): form = forms.AboutForm(request.POST) if form.is_valid(): form.save(request.journal) - messages.add_message(request, messages.INFO, 'Settings updated.') - return redirect(reverse('journal_description')) + messages.add_message(request, messages.INFO, "Settings updated.") + return redirect(reverse("journal_description")) - template = 'about_settings.html' + template = "about_settings.html" context = { - 'form': form, + "form": form, } return render(request, template, context) diff --git a/src/core/homepage_elements/carousel/admin.py b/src/core/homepage_elements/carousel/admin.py index a325435f9f..0445fcdb4c 100755 --- a/src/core/homepage_elements/carousel/admin.py +++ b/src/core/homepage_elements/carousel/admin.py @@ -4,11 +4,20 @@ class CarouselAdmin(admin.ModelAdmin): - list_display = ('mode', 'exclude', 'latest_articles', - 'latest_news', 'current_issue', 'journal', - 'press') - list_filter = ('journal', 'press', 'mode', 'exclude') - filter_horizontal = ('articles', 'news_articles',) + list_display = ( + "mode", + "exclude", + "latest_articles", + "latest_news", + "current_issue", + "journal", + "press", + ) + list_filter = ("journal", "press", "mode", "exclude") + filter_horizontal = ( + "articles", + "news_articles", + ) admin_list = [ diff --git a/src/core/homepage_elements/carousel/forms.py b/src/core/homepage_elements/carousel/forms.py index 7f9c2aaeb8..7a169fecad 100755 --- a/src/core/homepage_elements/carousel/forms.py +++ b/src/core/homepage_elements/carousel/forms.py @@ -16,24 +16,24 @@ class CarouselForm(forms.ModelForm): carousel_article_limit = forms.IntegerField( required=True, - label='Maximum number of articles to show', + label="Maximum number of articles to show", ) carousel_news_limit = forms.IntegerField( required=True, - label='Maximum number of news items to show', + label="Maximum number of news items to show", ) carousel_latest_articles = forms.BooleanField( required=False, - label='Display the latest published articles', + label="Display the latest published articles", ) carousel_articles = forms.ModelMultipleChoiceField( queryset=submission_models.Article.objects.none(), required=False, - widget=FilteredSelectMultiple("Article", False, attrs={'rows': '2'}) + widget=FilteredSelectMultiple("Article", False, attrs={"rows": "2"}), ) carousel_latest_news = forms.BooleanField( required=False, - label='Display the latest news items', + label="Display the latest news items", ) carousel_news = forms.ModelMultipleChoiceField( queryset=comms_models.NewsItem.objects.none(), @@ -41,57 +41,55 @@ class CarouselForm(forms.ModelForm): widget=FilteredSelectMultiple( "News Article", False, - attrs={'rows': '2'}, - ) + attrs={"rows": "2"}, + ), ) carousel_current_issue = forms.BooleanField( required=False, - label='Display the current issue', + label="Display the current issue", ) carousel_issues = forms.ModelMultipleChoiceField( queryset=journal_models.Issue.objects.none(), required=False, - widget=FilteredSelectMultiple("Issue", False, attrs={'rows': '2'}) + widget=FilteredSelectMultiple("Issue", False, attrs={"rows": "2"}), ) class Meta: model = models.Carousel - fields = ('latest_articles', 'latest_news', 'exclude', 'current_issue') - + fields = ("latest_articles", "latest_news", "exclude", "current_issue") def __init__(self, *args, **kwargs): - request = kwargs.pop('request', None) + request = kwargs.pop("request", None) article_list, news_list, issues_list = self.load(request) super(CarouselForm, self).__init__(*args, **kwargs) if request.site_type and request.site_type.carousel is not None: - self.initial['carousel_articles'] = article_list - self.initial['carousel_article_limit'] = request.site_type.carousel.article_limit - self.initial['carousel_news_limit'] = request.site_type.carousel.news_limit - self.initial['carousel_news'] = news_list - self.initial['carousel_issues'] = issues_list + self.initial["carousel_articles"] = article_list + self.initial["carousel_article_limit"] = ( + request.site_type.carousel.article_limit + ) + self.initial["carousel_news_limit"] = request.site_type.carousel.news_limit + self.initial["carousel_news"] = news_list + self.initial["carousel_issues"] = issues_list published_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_PUBLISHED, - date_published__lte=timezone.now() + stage=submission_models.STAGE_PUBLISHED, date_published__lte=timezone.now() ) if request.journal: - published_articles = published_articles.filter( - journal=request.journal - ) + published_articles = published_articles.filter(journal=request.journal) - self.fields['carousel_articles'].queryset = published_articles + self.fields["carousel_articles"].queryset = published_articles news_items = comms_models.NewsItem.objects.filter( content_type=request.model_content_type, object_id=request.site_type.pk, ) - self.fields['carousel_news'].queryset = news_items - self.fields['carousel_issues'].queryset = request.site_type.issues + self.fields["carousel_news"].queryset = news_items + self.fields["carousel_issues"].queryset = request.site_type.issues def load(self, request): # if no carousel set, create one @@ -153,6 +151,5 @@ def save(self, request, commit=True): messages.add_message( request, messages.SUCCESS, - 'Journal carousel settings saved', + "Journal carousel settings saved", ) - diff --git a/src/core/homepage_elements/carousel/hooks.py b/src/core/homepage_elements/carousel/hooks.py index 839a4f6f16..f08e528d15 100755 --- a/src/core/homepage_elements/carousel/hooks.py +++ b/src/core/homepage_elements/carousel/hooks.py @@ -1,8 +1,9 @@ - def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter(name='Carousel').exists(): + if ( + homepage_elements is not None + and homepage_elements.filter(name="Carousel").exists() + ): active_carousel, carousel_items = request.site_type.active_carousel - return {'carousel_items': carousel_items, - 'carousel': active_carousel} + return {"carousel_items": carousel_items, "carousel": active_carousel} else: return {} diff --git a/src/core/homepage_elements/carousel/migrations/0001_initial.py b/src/core/homepage_elements/carousel/migrations/0001_initial.py index 6556a9cb23..a37cbe22bf 100755 --- a/src/core/homepage_elements/carousel/migrations/0001_initial.py +++ b/src/core/homepage_elements/carousel/migrations/0001_initial.py @@ -6,30 +6,68 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Carousel', + name="Carousel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mode', models.CharField(choices=[('off', 'Off'), ('latest', 'Latest Articles'), ('news', 'News'), ('selected-articles', 'Selected Articles'), ('mixed', 'Latest Articles and News'), ('mixed-selected', 'Selected Articles and News')], default='Latest', max_length=200)), - ('exclude', models.BooleanField(default=False)), - ('article_limit', models.IntegerField(default=3, verbose_name='Maximum Number of Articles to Show')), - ('news_limit', models.IntegerField(default=0, verbose_name='Maximum Number of News Items to Show')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "mode", + models.CharField( + choices=[ + ("off", "Off"), + ("latest", "Latest Articles"), + ("news", "News"), + ("selected-articles", "Selected Articles"), + ("mixed", "Latest Articles and News"), + ("mixed-selected", "Selected Articles and News"), + ], + default="Latest", + max_length=200, + ), + ), + ("exclude", models.BooleanField(default=False)), + ( + "article_limit", + models.IntegerField( + default=3, verbose_name="Maximum Number of Articles to Show" + ), + ), + ( + "news_limit", + models.IntegerField( + default=0, verbose_name="Maximum Number of News Items to Show" + ), + ), ], ), migrations.CreateModel( - name='CarouselObject', + name="CarouselObject", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.CharField(blank=True, max_length=5000, null=True)), - ('title', models.CharField(max_length=300)), - ('index', models.IntegerField(default=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("url", models.CharField(blank=True, max_length=5000, null=True)), + ("title", models.CharField(max_length=300)), + ("index", models.IntegerField(default=1)), ], ), ] diff --git a/src/core/homepage_elements/carousel/migrations/0002_auto_20170711_1203.py b/src/core/homepage_elements/carousel/migrations/0002_auto_20170711_1203.py index cf687d2fa6..29a48f8c7b 100755 --- a/src/core/homepage_elements/carousel/migrations/0002_auto_20170711_1203.py +++ b/src/core/homepage_elements/carousel/migrations/0002_auto_20170711_1203.py @@ -7,29 +7,37 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('core', '0001_initial'), - ('submission', '0001_initial'), - ('carousel', '0001_initial'), + ("core", "0001_initial"), + ("submission", "0001_initial"), + ("carousel", "0001_initial"), ] operations = [ migrations.AddField( - model_name='carouselobject', - name='articleID', - field=models.ManyToManyField(blank=True, null=True, to='submission.Article'), + model_name="carouselobject", + name="articleID", + field=models.ManyToManyField( + blank=True, null=True, to="submission.Article" + ), ), migrations.AddField( - model_name='carouselobject', - name='large_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.File'), + model_name="carouselobject", + name="large_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.File", + ), ), migrations.AddField( - model_name='carousel', - name='articles', - field=models.ManyToManyField(blank=True, null=True, related_name='articles', to='submission.Article'), + model_name="carousel", + name="articles", + field=models.ManyToManyField( + blank=True, null=True, related_name="articles", to="submission.Article" + ), ), ] diff --git a/src/core/homepage_elements/carousel/migrations/0003_auto_20200121_1020.py b/src/core/homepage_elements/carousel/migrations/0003_auto_20200121_1020.py index 3a9365613c..7eaec73e72 100644 --- a/src/core/homepage_elements/carousel/migrations/0003_auto_20200121_1020.py +++ b/src/core/homepage_elements/carousel/migrations/0003_auto_20200121_1020.py @@ -6,21 +6,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('comms', '0002_auto_20170816_1050'), - ('carousel', '0002_auto_20170711_1203'), + ("comms", "0002_auto_20170816_1050"), + ("carousel", "0002_auto_20170711_1203"), ] operations = [ migrations.AddField( - model_name='carousel', - name='news_articles', - field=models.ManyToManyField(blank=True, to='comms.NewsItem'), + model_name="carousel", + name="news_articles", + field=models.ManyToManyField(blank=True, to="comms.NewsItem"), ), migrations.AlterField( - model_name='carousel', - name='mode', - field=models.CharField(choices=[('off', 'Off'), ('latest', 'Latest Articles'), ('news', 'Latest News'), ('selected-articles', 'Selected Articles'), ('mixed', 'Latest Articles and News'), ('mixed-selected', 'Selected Articles and News')], default='Latest', max_length=200), + model_name="carousel", + name="mode", + field=models.CharField( + choices=[ + ("off", "Off"), + ("latest", "Latest Articles"), + ("news", "Latest News"), + ("selected-articles", "Selected Articles"), + ("mixed", "Latest Articles and News"), + ("mixed-selected", "Selected Articles and News"), + ], + default="Latest", + max_length=200, + ), ), ] diff --git a/src/core/homepage_elements/carousel/migrations/0004_auto_20211018_1747.py b/src/core/homepage_elements/carousel/migrations/0004_auto_20211018_1747.py index f0925b8e7d..ded73beebc 100644 --- a/src/core/homepage_elements/carousel/migrations/0004_auto_20211018_1747.py +++ b/src/core/homepage_elements/carousel/migrations/0004_auto_20211018_1747.py @@ -21,37 +21,49 @@ def migrate_carousel_mode(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0046_auto_20210922_1436'), - ('carousel', '0003_auto_20200121_1020'), + ("journal", "0046_auto_20210922_1436"), + ("carousel", "0003_auto_20200121_1020"), ] operations = [ migrations.AddField( - model_name='carousel', - name='current_issue', - field=models.BooleanField(default=False, help_text='Always include the current issue'), + model_name="carousel", + name="current_issue", + field=models.BooleanField( + default=False, help_text="Always include the current issue" + ), ), migrations.AddField( - model_name='carousel', - name='issues', - field=models.ManyToManyField(blank=True, to='journal.Issue', verbose_name='Issues and Collections'), + model_name="carousel", + name="issues", + field=models.ManyToManyField( + blank=True, to="journal.Issue", verbose_name="Issues and Collections" + ), ), migrations.AddField( - model_name='carousel', - name='latest_articles', - field=models.BooleanField(default=False, help_text='The carousel will display the latest published articles'), + model_name="carousel", + name="latest_articles", + field=models.BooleanField( + default=False, + help_text="The carousel will display the latest published articles", + ), ), migrations.AddField( - model_name='carousel', - name='latest_news', - field=models.BooleanField(default=False, help_text='The carousel will display the latest published news items'), + model_name="carousel", + name="latest_news", + field=models.BooleanField( + default=False, + help_text="The carousel will display the latest published news items", + ), ), migrations.AlterField( - model_name='carousel', - name='exclude', - field=models.BooleanField(default=False, help_text='If enabled, the selectors below will behave as an exclusion list'), + model_name="carousel", + name="exclude", + field=models.BooleanField( + default=False, + help_text="If enabled, the selectors below will behave as an exclusion list", + ), ), migrations.RunPython(migrate_carousel_mode, migrations.RunPython.noop), ] diff --git a/src/core/homepage_elements/carousel/migrations/0005_alter_carouselobject_large_image_file.py b/src/core/homepage_elements/carousel/migrations/0005_alter_carouselobject_large_image_file.py index d2dff67795..819b974c00 100644 --- a/src/core/homepage_elements/carousel/migrations/0005_alter_carouselobject_large_image_file.py +++ b/src/core/homepage_elements/carousel/migrations/0005_alter_carouselobject_large_image_file.py @@ -5,16 +5,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_auto_20230120_1546'), - ('carousel', '0004_auto_20211018_1747'), + ("core", "0078_auto_20230120_1546"), + ("carousel", "0004_auto_20211018_1747"), ] operations = [ migrations.AlterField( - model_name='carouselobject', - name='large_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.file'), + model_name="carouselobject", + name="large_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.file", + ), ), ] diff --git a/src/core/homepage_elements/carousel/models.py b/src/core/homepage_elements/carousel/models.py index 119baea897..eb557913aa 100755 --- a/src/core/homepage_elements/carousel/models.py +++ b/src/core/homepage_elements/carousel/models.py @@ -6,12 +6,12 @@ from django.utils.translation import gettext_lazy as _ CAROUSEL_MODES = [ - ('off', _('Off')), - ('latest', _('Latest Articles')), - ('news', _('Latest News')), - ('selected-articles', _('Selected Articles')), - ('mixed', _('Latest Articles and News')), - ('mixed-selected', _('Selected Articles and News')), + ("off", _("Off")), + ("latest", _("Latest Articles")), + ("news", _("Latest News")), + ("selected-articles", _("Selected Articles")), + ("mixed", _("Latest Articles and News")), + ("mixed-selected", _("Selected Articles and News")), ] @@ -20,7 +20,7 @@ class Carousel(models.Model): max_length=200, blank=False, null=False, - default='Latest', + default="Latest", choices=CAROUSEL_MODES, ) enabled = True @@ -39,10 +39,10 @@ class Carousel(models.Model): # these fields contains a custom list of articles and article-like carousel objects for Mixed and News modes articles = models.ManyToManyField( - 'submission.Article', + "submission.Article", blank=True, null=True, - related_name='articles', + related_name="articles", ) latest_news = models.BooleanField( @@ -52,21 +52,21 @@ class Carousel(models.Model): # a selected news field news_articles = models.ManyToManyField( - 'comms.NewsItem', + "comms.NewsItem", blank=True, ) # article and news limits article_limit = models.IntegerField( - verbose_name='Maximum Number of Articles to Show', + verbose_name="Maximum Number of Articles to Show", default=3, ) news_limit = models.IntegerField( - verbose_name='Maximum Number of News Items to Show', + verbose_name="Maximum Number of News Items to Show", default=0, ) issues = models.ManyToManyField( - 'journal.issue', + "journal.issue", verbose_name=_("Issues and Collections"), blank=True, ) @@ -78,6 +78,7 @@ class Carousel(models.Model): def get_items(self): import core.logic as core_logic + Article = apps.get_model("submission", "Article") Issue = apps.get_model("journal", "Issue") NewsItem = apps.get_model("comms", "NewsItem") @@ -87,12 +88,12 @@ def get_items(self): issues = Issue.objects.none() if self.latest_articles: - if hasattr(self, 'press'): - articles |= core_logic.latest_articles(self, 'press') - elif hasattr(self, 'journal'): - articles |= core_logic.latest_articles(self, 'journal') + if hasattr(self, "press"): + articles |= core_logic.latest_articles(self, "press") + elif hasattr(self, "journal"): + articles |= core_logic.latest_articles(self, "journal") if self.article_limit > 0: - articles = articles[:self.article_limit] + articles = articles[: self.article_limit] if self.articles.exists(): if self.exclude: @@ -101,12 +102,12 @@ def get_items(self): articles = chain(self.articles.all(), articles) if self.latest_news: - if hasattr(self, 'press'): - news |= core_logic.news_items(self, 'press') - elif hasattr(self, 'journal'): - news |= core_logic.news_items(self, 'journal') + if hasattr(self, "press"): + news |= core_logic.news_items(self, "press") + elif hasattr(self, "journal"): + news |= core_logic.news_items(self, "journal") if self.news_limit > 0: - news = news[:self.news_limit] + news = news[: self.news_limit] if self.news_articles.exists(): if self.exclude: @@ -128,11 +129,13 @@ def get_items(self): class CarouselObject(models.Model): - large_image_file = models.ForeignKey('core.File', null=True, blank=True, on_delete=models.SET_NULL) + large_image_file = models.ForeignKey( + "core.File", null=True, blank=True, on_delete=models.SET_NULL + ) url = models.CharField(max_length=5000, blank=True, null=True) title = models.CharField(max_length=300) index = models.IntegerField(default=1) - articleID = models.ManyToManyField('submission.Article', null=True, blank=True) + articleID = models.ManyToManyField("submission.Article", null=True, blank=True) @property def local_url(self): diff --git a/src/core/homepage_elements/carousel/plugin_settings.py b/src/core/homepage_elements/carousel/plugin_settings.py index ee762a92dd..a27d45053d 100755 --- a/src/core/homepage_elements/carousel/plugin_settings.py +++ b/src/core/homepage_elements/carousel/plugin_settings.py @@ -1,9 +1,9 @@ from django.db.utils import OperationalError from django.contrib.contenttypes.models import ContentType -PLUGIN_NAME = 'Carousel' -DESCRIPTION = 'This is a homepage element that renders a carousel.' -AUTHOR = 'Martin Paul Eve' +PLUGIN_NAME = "Carousel" +DESCRIPTION = "This is a homepage element that renders a carousel." +AUTHOR = "Martin Paul Eve" def install(): @@ -18,12 +18,13 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='carousel_settings', - template_path='journal/homepage_elements/carousel.html', + configure_url="carousel_settings", + template_path="journal/homepage_elements/carousel.html", content_type=content_type, object_id=journal.pk, has_config=True, - defaults={'available_to_press': True}) + defaults={"available_to_press": True}, + ) element.save() @@ -33,12 +34,13 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='carousel_settings', - template_path='journal/homepage_elements/carousel.html', + configure_url="carousel_settings", + template_path="journal/homepage_elements/carousel.html", content_type=content_type, object_id=press.pk, has_config=True, - defaults={'available_to_press': True}) + defaults={"available_to_press": True}, + ) element.save() @@ -47,14 +49,14 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.carousel.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.carousel.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: # if we get here the database hasn't yet been created return {} - except BaseException: # if we get here, well, something has gone very wrong + except BaseException: # if we get here, well, something has gone very wrong return {} diff --git a/src/core/homepage_elements/carousel/tests.py b/src/core/homepage_elements/carousel/tests.py index 91e5c3f2d9..157efd9878 100644 --- a/src/core/homepage_elements/carousel/tests.py +++ b/src/core/homepage_elements/carousel/tests.py @@ -14,12 +14,16 @@ from submission import models as sm_models from utils.testing.helpers import create_journals -FROZEN_DATETIME_20180628 = timezone.make_aware(timezone.datetime(2018, 6, 28, 8, 15, 27, 243860)) -FROZEN_DATETIME_20180629 = timezone.make_aware(timezone.datetime(2018, 6, 29, 8, 15, 27, 243860)) +FROZEN_DATETIME_20180628 = timezone.make_aware( + timezone.datetime(2018, 6, 28, 8, 15, 27, 243860) +) +FROZEN_DATETIME_20180629 = timezone.make_aware( + timezone.datetime(2018, 6, 29, 8, 15, 27, 243860) +) + # Create your tests here. class TestCarousel(TestCase): - @classmethod def setUpTestData(cls): cls.journal_one, cls.journal_2 = create_journals() @@ -27,20 +31,19 @@ def setUpTestData(cls): cls.news_item = comms_models.NewsItem.objects.create( posted=FROZEN_DATETIME_20180628, ) - cls.news_item.content_type = ContentType.objects.get_for_model( - cls.journal_one) + cls.news_item.content_type = ContentType.objects.get_for_model(cls.journal_one) cls.news_item.object_id = cls.journal_one.id cls.article = sm_models.Article.objects.create( journal=cls.journal_one, stage=sm_models.STAGE_PUBLISHED, date_published=FROZEN_DATETIME_20180628, - title='Carousel Article One', + title="Carousel Article One", ) cls.article = sm_models.Article.objects.create( journal=cls.journal_one, stage=sm_models.STAGE_PUBLISHED, date_published=FROZEN_DATETIME_20180629, - title='Carousel Article Two', + title="Carousel Article Two", ) def test_carousel(self): diff --git a/src/core/homepage_elements/carousel/urls.py b/src/core/homepage_elements/carousel/urls.py index 7b1ba5d6cf..2e8a382ee2 100755 --- a/src/core/homepage_elements/carousel/urls.py +++ b/src/core/homepage_elements/carousel/urls.py @@ -2,5 +2,5 @@ from core.homepage_elements.carousel import views urlpatterns = [ - re_path(r'^settings/$', views.settings_carousel, name='carousel_settings'), + re_path(r"^settings/$", views.settings_carousel, name="carousel_settings"), ] diff --git a/src/core/homepage_elements/carousel/views.py b/src/core/homepage_elements/carousel/views.py index 045b6db28a..a68b3855fb 100755 --- a/src/core/homepage_elements/carousel/views.py +++ b/src/core/homepage_elements/carousel/views.py @@ -13,8 +13,8 @@ def settings_carousel(request): instance=request.site_type.carousel, ) - if request.POST and 'cancel' in request.POST: - return redirect(reverse('core_manager_index')) + if request.POST and "cancel" in request.POST: + return redirect(reverse("core_manager_index")) if request.POST: home_form = forms.CarouselForm( @@ -26,13 +26,15 @@ def settings_carousel(request): if home_form.is_valid(): home_form.save(request=request) - return redirect(reverse('home_settings_index')) + return redirect(reverse("home_settings_index")) - template = 'carousel_setup.html' + template = "carousel_setup.html" context = { - 'settings': [{group.name: models.Setting.objects.filter(group=group).order_by('name')} for group in - models.SettingGroup.objects.all().order_by('name')], - 'home_form': home_form + "settings": [ + {group.name: models.Setting.objects.filter(group=group).order_by("name")} + for group in models.SettingGroup.objects.all().order_by("name") + ], + "home_form": home_form, } return render(request, template, context) diff --git a/src/core/homepage_elements/featured/admin.py b/src/core/homepage_elements/featured/admin.py index 4d3500e33f..035b6a5e17 100755 --- a/src/core/homepage_elements/featured/admin.py +++ b/src/core/homepage_elements/featured/admin.py @@ -4,11 +4,11 @@ class FeaturedArticleAdmin(admin.ModelAdmin): - list_display = ('article', 'sequence', 'journal', 'added', 'added_by') - list_filter = ('journal', 'added') - search_fields = ('article__pk', 'article__title') - raw_id_fields = ('article', 'added_by') - date_hierarchy = ('added') + list_display = ("article", "sequence", "journal", "added", "added_by") + list_filter = ("journal", "added") + search_fields = ("article__pk", "article__title") + raw_id_fields = ("article", "added_by") + date_hierarchy = "added" admin_list = [ diff --git a/src/core/homepage_elements/featured/hooks.py b/src/core/homepage_elements/featured/hooks.py index 410e814a95..c214302d1f 100755 --- a/src/core/homepage_elements/featured/hooks.py +++ b/src/core/homepage_elements/featured/hooks.py @@ -2,9 +2,14 @@ def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter(name='Featured Articles').exists(): - featured_articles = models.FeaturedArticle.objects.filter(journal=request.journal) + if ( + homepage_elements is not None + and homepage_elements.filter(name="Featured Articles").exists() + ): + featured_articles = models.FeaturedArticle.objects.filter( + journal=request.journal + ) - return {'featured_articles': featured_articles} + return {"featured_articles": featured_articles} else: return {} diff --git a/src/core/homepage_elements/featured/migrations/0001_initial.py b/src/core/homepage_elements/featured/migrations/0001_initial.py index 951100434b..f2db3a71d4 100755 --- a/src/core/homepage_elements/featured/migrations/0001_initial.py +++ b/src/core/homepage_elements/featured/migrations/0001_initial.py @@ -9,7 +9,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [ @@ -18,15 +17,29 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='FeaturedArticle', + name="FeaturedArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sequence', models.PositiveIntegerField(default=999)), - ('added', models.DateTimeField(default=django.utils.timezone.now)), - ('added_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("sequence", models.PositiveIntegerField(default=999)), + ("added", models.DateTimeField(default=django.utils.timezone.now)), + ( + "added_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('sequence', 'added'), + "ordering": ("sequence", "added"), }, ), ] diff --git a/src/core/homepage_elements/featured/migrations/0002_auto_20170711_1203.py b/src/core/homepage_elements/featured/migrations/0002_auto_20170711_1203.py index 7fdeba509c..a615c3fb9c 100755 --- a/src/core/homepage_elements/featured/migrations/0002_auto_20170711_1203.py +++ b/src/core/homepage_elements/featured/migrations/0002_auto_20170711_1203.py @@ -7,24 +7,27 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('journal', '0001_initial'), - ('featured', '0001_initial'), - ('submission', '0001_initial'), + ("journal", "0001_initial"), + ("featured", "0001_initial"), + ("submission", "0001_initial"), ] operations = [ migrations.AddField( - model_name='featuredarticle', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="featuredarticle", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='featuredarticle', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="featuredarticle", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), ] diff --git a/src/core/homepage_elements/featured/migrations/0003_alter_featuredarticle_added_by.py b/src/core/homepage_elements/featured/migrations/0003_alter_featuredarticle_added_by.py index 30f1af9542..1ac96a82dd 100644 --- a/src/core/homepage_elements/featured/migrations/0003_alter_featuredarticle_added_by.py +++ b/src/core/homepage_elements/featured/migrations/0003_alter_featuredarticle_added_by.py @@ -6,16 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('featured', '0002_auto_20170711_1203'), + ("featured", "0002_auto_20170711_1203"), ] operations = [ migrations.AlterField( - model_name='featuredarticle', - name='added_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="featuredarticle", + name="added_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/core/homepage_elements/featured/models.py b/src/core/homepage_elements/featured/models.py index c336b9c3cf..a8a37346c4 100755 --- a/src/core/homepage_elements/featured/models.py +++ b/src/core/homepage_elements/featured/models.py @@ -4,23 +4,23 @@ class FeaturedArticle(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) sequence = models.PositiveIntegerField(default=999) added = models.DateTimeField(default=timezone.now) added_by = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) class Meta: - ordering = ('sequence', 'added') + ordering = ("sequence", "added") def __str__(self): return "{0} - {1}".format(self.article.title, self.journal) diff --git a/src/core/homepage_elements/featured/plugin_settings.py b/src/core/homepage_elements/featured/plugin_settings.py index b5fbbb9e3f..25d2ca21d6 100755 --- a/src/core/homepage_elements/featured/plugin_settings.py +++ b/src/core/homepage_elements/featured/plugin_settings.py @@ -1,9 +1,9 @@ from django.db.utils import OperationalError from django.contrib.contenttypes.models import ContentType -PLUGIN_NAME = 'Featured Articles' -DESCRIPTION = 'This is a homepage element that renders featured articles.' -AUTHOR = 'Martin Paul Eve' +PLUGIN_NAME = "Featured Articles" +DESCRIPTION = "This is a homepage element that renders featured articles." +AUTHOR = "Martin Paul Eve" def install(): @@ -18,11 +18,12 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='featured_articles_setup', - template_path='journal/homepage_elements/featured.html', + configure_url="featured_articles_setup", + template_path="journal/homepage_elements/featured.html", content_type=content_type, object_id=journal.pk, - has_config=True) + has_config=True, + ) element.save() @@ -32,11 +33,12 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='featured_articles_setup', - template_path='journal/homepage_elements/featured.html', + configure_url="featured_articles_setup", + template_path="journal/homepage_elements/featured.html", content_type=content_type, object_id=press.pk, - has_config=True) + has_config=True, + ) element.save() @@ -45,10 +47,10 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.featured.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.featured.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/featured/urls.py b/src/core/homepage_elements/featured/urls.py index 94730cd1db..f02ae4c53f 100755 --- a/src/core/homepage_elements/featured/urls.py +++ b/src/core/homepage_elements/featured/urls.py @@ -3,6 +3,6 @@ urlpatterns = [ # Featured Articles - re_path(r'^manager/$', views.featured_articles, name='featured_articles_setup'), - re_path(r'^manager/order/$', views.featured_articles_order, name='featured_order'), + re_path(r"^manager/$", views.featured_articles, name="featured_articles_setup"), + re_path(r"^manager/order/$", views.featured_articles_order, name="featured_order"), ] diff --git a/src/core/homepage_elements/featured/views.py b/src/core/homepage_elements/featured/views.py index e2d0f90da6..a0a4c8a5ea 100755 --- a/src/core/homepage_elements/featured/views.py +++ b/src/core/homepage_elements/featured/views.py @@ -9,36 +9,39 @@ @editor_user_required def featured_articles(request): - featured_arts = models.FeaturedArticle.objects.filter(journal=request.journal) featured_article_pks = [f.article.pk for f in featured_arts.all()] - articles = submission_models.Article.objects.filter(date_published__isnull=False, - journal=request.journal).exclude(pk__in=featured_article_pks) - - if 'article_id' in request.POST: - article_id = request.POST.get('article_id') - article = get_object_or_404(submission_models.Article, pk=article_id, journal=request.journal) + articles = submission_models.Article.objects.filter( + date_published__isnull=False, journal=request.journal + ).exclude(pk__in=featured_article_pks) + + if "article_id" in request.POST: + article_id = request.POST.get("article_id") + article = get_object_or_404( + submission_models.Article, pk=article_id, journal=request.journal + ) models.FeaturedArticle.objects.create( article=article, journal=request.journal, added_by=request.user, - sequence=request.journal.next_featured_article_order() + sequence=request.journal.next_featured_article_order(), ) - return redirect(reverse('featured_articles_setup')) + return redirect(reverse("featured_articles_setup")) - if 'delete' in request.POST: - article_id = request.POST.get('delete') - featured_article = get_object_or_404(models.FeaturedArticle, article__pk=article_id, - journal=request.journal) + if "delete" in request.POST: + article_id = request.POST.get("delete") + featured_article = get_object_or_404( + models.FeaturedArticle, article__pk=article_id, journal=request.journal + ) featured_article.delete() - return redirect(reverse('featured_articles_setup')) + return redirect(reverse("featured_articles_setup")) - template = 'featured_setup.html' + template = "featured_setup.html" context = { - 'featured_articles': featured_arts, - 'articles': articles, + "featured_articles": featured_arts, + "articles": articles, } return render(request, template, context) @@ -47,11 +50,11 @@ def featured_articles(request): @editor_user_required def featured_articles_order(request): if request.POST: - ids = request.POST.getlist('article[]') + ids = request.POST.getlist("article[]") ids = [int(_id) for _id in ids] for fa in models.FeaturedArticle.objects.filter(journal=request.journal): fa.sequence = ids.index(fa.pk) fa.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") diff --git a/src/core/homepage_elements/html/forms.py b/src/core/homepage_elements/html/forms.py index da188cbdf0..b6fe6e3e8e 100644 --- a/src/core/homepage_elements/html/forms.py +++ b/src/core/homepage_elements/html/forms.py @@ -8,16 +8,16 @@ class HTMLForm(forms.Form): html_content = forms.CharField( widget=TinyMCE, - label='HTML Content', + label="HTML Content", ) def save(self, journal, commit=True): - html_content = self.cleaned_data.get('html_content') + html_content = self.cleaned_data.get("html_content") if commit: setting_handler.save_plugin_setting( plugin_settings.get_self(), - 'html_block_content', + "html_block_content", html_content, journal, ) diff --git a/src/core/homepage_elements/html/hooks.py b/src/core/homepage_elements/html/hooks.py index ce4fc87d44..ed59e969fc 100755 --- a/src/core/homepage_elements/html/hooks.py +++ b/src/core/homepage_elements/html/hooks.py @@ -10,11 +10,13 @@ def yield_homepage_element_context(request, homepage_elements): - plugin = models.Plugin.objects.get(name='HTML') + plugin = models.Plugin.objects.get(name="HTML") try: - html_block_content = setting_handler.get_plugin_setting(plugin, 'html_block_content', request.journal).value + html_block_content = setting_handler.get_plugin_setting( + plugin, "html_block_content", request.journal + ).value except AttributeError as e: logger.exception(e) - html_block_content = '

This element has no content.

' + html_block_content = "

This element has no content.

" - return {'html_content': html_block_content} + return {"html_content": html_block_content} diff --git a/src/core/homepage_elements/html/plugin_settings.py b/src/core/homepage_elements/html/plugin_settings.py index ace1f6ff6f..9c6894bef5 100755 --- a/src/core/homepage_elements/html/plugin_settings.py +++ b/src/core/homepage_elements/html/plugin_settings.py @@ -8,9 +8,9 @@ from utils import models, setting_handler -PLUGIN_NAME = 'HTML' -DESCRIPTION = 'This is a homepage element that renders an HTML block' -AUTHOR = 'Andy Byers' +PLUGIN_NAME = "HTML" +DESCRIPTION = "This is a homepage element that renders an HTML block" +AUTHOR = "Andy Byers" VERSION = 1.0 @@ -32,27 +32,27 @@ def install(): name=PLUGIN_NAME, version=VERSION, enabled=True, - display_name='HTML', + display_name="HTML", press_wide=True, homepage_element=True, ) - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=plugin.name) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=plugin.name) setting_group, c = core_models.SettingGroup.objects.get_or_create( name=plugin_group_name, ) setting, c = core_models.Setting.objects.get_or_create( - name='html_block_content', + name="html_block_content", group=setting_group, defaults={ - 'pretty_name': 'HTML Block Content', - 'types': 'rich-text', - 'description': DESCRIPTION, - 'is_translatable': True, - } + "pretty_name": "HTML Block Content", + "types": "rich-text", + "description": DESCRIPTION, + "is_translatable": True, + }, ) setting_handler.get_or_create_default_setting( setting, - default_value='

This element has no content.

', + default_value="

This element has no content.

", ) # check whether this homepage element has @@ -63,11 +63,12 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='html_settings', - template_path='journal/homepage_elements/html_block.html', + configure_url="html_settings", + template_path="journal/homepage_elements/html_block.html", content_type=content_type, object_id=journal.pk, - has_config=True) + has_config=True, + ) element.save() @@ -77,22 +78,26 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='html_settings', - template_path='journal/homepage_elements/html_block.html', + configure_url="html_settings", + template_path="journal/homepage_elements/html_block.html", content_type=content_type, object_id=press.pk, has_config=True, - available_to_press=True) + available_to_press=True, + ) element.save() def hook_registry(): try: - return {'yield_homepage_element_context': {'module': 'core.homepage_elements.html.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME,} - } + return { + "yield_homepage_element_context": { + "module": "core.homepage_elements.html.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, + } + } except OperationalError: # if we get here the database hasn't yet been created return {} diff --git a/src/core/homepage_elements/html/urls.py b/src/core/homepage_elements/html/urls.py index 9c4029cdc1..1be0f9e4b5 100755 --- a/src/core/homepage_elements/html/urls.py +++ b/src/core/homepage_elements/html/urls.py @@ -8,5 +8,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.html_settings, name='html_settings'), + re_path(r"^$", views.html_settings, name="html_settings"), ] diff --git a/src/core/homepage_elements/html/views.py b/src/core/homepage_elements/html/views.py index 15d7d37f34..e3defbd261 100755 --- a/src/core/homepage_elements/html/views.py +++ b/src/core/homepage_elements/html/views.py @@ -14,19 +14,19 @@ @editor_user_required def html_settings(request): - plugin = models.Plugin.objects.get(name='HTML') + plugin = models.Plugin.objects.get(name="HTML") html_content = setting_handler.get_plugin_setting( plugin, - 'html_block_content', + "html_block_content", request.journal, create=True, - pretty='HTML Block Content' + pretty="HTML Block Content", ).value form = forms.HTMLForm( initial={ - 'html_content': html_content, + "html_content": html_content, } ) @@ -36,12 +36,12 @@ def html_settings(request): ) if form.is_valid(): form.save(request.journal) - messages.add_message(request, messages.INFO, 'HTML Block updated.') - return redirect(reverse('home_settings_index')) + messages.add_message(request, messages.INFO, "HTML Block updated.") + return redirect(reverse("home_settings_index")) - template = 'html_settings.html' + template = "html_settings.html" context = { - 'form': form, + "form": form, } return render(request, template, context) diff --git a/src/core/homepage_elements/issue/hooks.py b/src/core/homepage_elements/issue/hooks.py index 136acebc56..87d7749190 100755 --- a/src/core/homepage_elements/issue/hooks.py +++ b/src/core/homepage_elements/issue/hooks.py @@ -2,30 +2,31 @@ def yield_homepage_element_context(request, homepage_elements): - """ Renders a specific issue in the journal. - - :param request: the request associated with this call - :return: a rendered template of this issue - """ - - if homepage_elements is not None and homepage_elements.filter( - name='Current Issue', - ).exists(): - + """Renders a specific issue in the journal. + + :param request: the request associated with this call + :return: a rendered template of this issue + """ + + if ( + homepage_elements is not None + and homepage_elements.filter( + name="Current Issue", + ).exists() + ): issue_object = request.journal.current_issue if issue_object: - issue_objects = models.Issue.objects.filter( journal=request.journal, ) context = { - 'issue': issue_object, - 'structure': issue_object.structure, # for compatibility - 'issues': issue_objects, - 'articles': issue_object.get_sorted_articles(), - 'show_sidebar': False, + "issue": issue_object, + "structure": issue_object.structure, # for compatibility + "issues": issue_objects, + "articles": issue_object.get_sorted_articles(), + "show_sidebar": False, } return context diff --git a/src/core/homepage_elements/issue/plugin_settings.py b/src/core/homepage_elements/issue/plugin_settings.py index 133586170a..2e6ca783e1 100755 --- a/src/core/homepage_elements/issue/plugin_settings.py +++ b/src/core/homepage_elements/issue/plugin_settings.py @@ -1,9 +1,9 @@ from django.db.utils import OperationalError from django.contrib.contenttypes.models import ContentType -PLUGIN_NAME = 'Current Issue' -DESCRIPTION = 'This is a homepage element that renders featured current issues.' -AUTHOR = 'Martin Paul Eve' +PLUGIN_NAME = "Current Issue" +DESCRIPTION = "This is a homepage element that renders featured current issues." +AUTHOR = "Martin Paul Eve" def install(): @@ -18,11 +18,12 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='current_issue_setup', - template_path='journal/homepage_elements/issue_block.html', + configure_url="current_issue_setup", + template_path="journal/homepage_elements/issue_block.html", content_type=content_type, object_id=journal.pk, - has_config=True) + has_config=True, + ) element.save() @@ -32,11 +33,12 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='current_issue_setup', - template_path='journal/homepage_elements/issue_block.html', + configure_url="current_issue_setup", + template_path="journal/homepage_elements/issue_block.html", content_type=content_type, object_id=press.pk, - has_config=True) + has_config=True, + ) element.save() @@ -45,10 +47,10 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.issue.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.issue.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/issue/urls.py b/src/core/homepage_elements/issue/urls.py index 66e988beeb..f86d46f5a4 100755 --- a/src/core/homepage_elements/issue/urls.py +++ b/src/core/homepage_elements/issue/urls.py @@ -3,5 +3,7 @@ urlpatterns = [ # Featured Articles - re_path(r'^manager/currentissue/$', views.current_issue, name='current_issue_setup'), + re_path( + r"^manager/currentissue/$", views.current_issue, name="current_issue_setup" + ), ] diff --git a/src/core/homepage_elements/issue/views.py b/src/core/homepage_elements/issue/views.py index 6e1b95eacf..b66864328a 100755 --- a/src/core/homepage_elements/issue/views.py +++ b/src/core/homepage_elements/issue/views.py @@ -6,4 +6,4 @@ @editor_user_required def current_issue(request): - return redirect(reverse('home_settings_index')) + return redirect(reverse("home_settings_index")) diff --git a/src/core/homepage_elements/journals/forms.py b/src/core/homepage_elements/journals/forms.py index 24a4a0f592..2fc11a2cf1 100644 --- a/src/core/homepage_elements/journals/forms.py +++ b/src/core/homepage_elements/journals/forms.py @@ -7,13 +7,13 @@ class FeaturedJournalsForm(forms.ModelForm): class Meta: model = models.Press - fields = ('random_featured_journals', 'featured_journals') + fields = ("random_featured_journals", "featured_journals") widgets = { - 'featured_journals': forms.CheckboxSelectMultiple, + "featured_journals": forms.CheckboxSelectMultiple, } def __init__(self, *args, **kwargs): super(FeaturedJournalsForm, self).__init__(*args, **kwargs) journals = jm.Journal.objects.filter(hide_from_press=False) - self.fields['featured_journals'].queryset = journals + self.fields["featured_journals"].queryset = journals diff --git a/src/core/homepage_elements/journals/hooks.py b/src/core/homepage_elements/journals/hooks.py index 8c749352bb..f03c51b4bd 100755 --- a/src/core/homepage_elements/journals/hooks.py +++ b/src/core/homepage_elements/journals/hooks.py @@ -20,13 +20,15 @@ def get_random_journals(): def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter(name='Journals').exists(): - + if ( + homepage_elements is not None + and homepage_elements.filter(name="Journals").exists() + ): if request.press.random_featured_journals: featured_journals = get_random_journals() else: featured_journals = request.press.featured_journals.all() - return {'featured_journals': sorted(featured_journals, key=lambda x: x.name)} + return {"featured_journals": sorted(featured_journals, key=lambda x: x.name)} else: return {} diff --git a/src/core/homepage_elements/journals/plugin_settings.py b/src/core/homepage_elements/journals/plugin_settings.py index b00dba0dc8..d1617551bc 100755 --- a/src/core/homepage_elements/journals/plugin_settings.py +++ b/src/core/homepage_elements/journals/plugin_settings.py @@ -6,8 +6,8 @@ from django.db.utils import OperationalError from django.contrib.contenttypes.models import ContentType -PLUGIN_NAME = 'Journals' -DESCRIPTION = 'This plugin displays featured journals.' +PLUGIN_NAME = "Journals" +DESCRIPTION = "This plugin displays featured journals." def install(): @@ -20,12 +20,13 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='featured_journals', - template_path='journal/homepage_elements/journals.html', + configure_url="featured_journals", + template_path="journal/homepage_elements/journals.html", content_type=content_type, object_id=press.pk, has_config=True, - available_to_press=True) + available_to_press=True, + ) element.save() @@ -34,10 +35,10 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.journals.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.journals.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/journals/urls.py b/src/core/homepage_elements/journals/urls.py index 2f2858e63d..bd5bdc07c2 100755 --- a/src/core/homepage_elements/journals/urls.py +++ b/src/core/homepage_elements/journals/urls.py @@ -8,5 +8,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.featured_journals, name='featured_journals'), + re_path(r"^$", views.featured_journals, name="featured_journals"), ] diff --git a/src/core/homepage_elements/journals/views.py b/src/core/homepage_elements/journals/views.py index c5a8335638..3165cb2b92 100755 --- a/src/core/homepage_elements/journals/views.py +++ b/src/core/homepage_elements/journals/views.py @@ -18,23 +18,13 @@ def featured_journals(request): form = forms.FeaturedJournalsForm(request.POST, instance=request.press) if form.is_valid(): form.save() - messages.add_message( - request, - messages.SUCCESS, - 'Saved.' - ) - - return redirect( - reverse( - 'home_settings_index' - ) - ) - - template = 'featured_journals.html' + messages.add_message(request, messages.SUCCESS, "Saved.") + + return redirect(reverse("home_settings_index")) + + template = "featured_journals.html" context = { - 'form': form, + "form": form, } return render(request, template, context) - - diff --git a/src/core/homepage_elements/journals_and_html/forms.py b/src/core/homepage_elements/journals_and_html/forms.py index 8226d3eb79..5c3a4a1ece 100644 --- a/src/core/homepage_elements/journals_and_html/forms.py +++ b/src/core/homepage_elements/journals_and_html/forms.py @@ -6,31 +6,28 @@ class JournalsHTMLForm(forms.ModelForm): - html_content = forms.CharField( - widget=TinyMCE, - label='HTML Content' - ) + html_content = forms.CharField(widget=TinyMCE, label="HTML Content") class Meta: model = models.Press - fields = ('random_featured_journals', 'featured_journals') + fields = ("random_featured_journals", "featured_journals") widgets = { - 'featured_journals': forms.CheckboxSelectMultiple, + "featured_journals": forms.CheckboxSelectMultiple, } def __init__(self, *args, **kwargs): - self.html_setting = kwargs.pop('html_setting', None) + self.html_setting = kwargs.pop("html_setting", None) super(JournalsHTMLForm, self).__init__(*args, **kwargs) journals = jm.Journal.objects.filter(hide_from_press=False) - self.fields['featured_journals'].queryset = journals - self.fields['html_content'].initial = self.html_setting.value + self.fields["featured_journals"].queryset = journals + self.fields["html_content"].initial = self.html_setting.value def save(self, commit=True): press = super(JournalsHTMLForm, self).save(commit=False) if self.html_setting: - self.html_setting.value = self.cleaned_data.get('html_content', '') + self.html_setting.value = self.cleaned_data.get("html_content", "") if commit: self.html_setting.save() diff --git a/src/core/homepage_elements/journals_and_html/hooks.py b/src/core/homepage_elements/journals_and_html/hooks.py index 09a73aee1a..44e81eaccd 100755 --- a/src/core/homepage_elements/journals_and_html/hooks.py +++ b/src/core/homepage_elements/journals_and_html/hooks.py @@ -24,10 +24,12 @@ def get_random_journals(): def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter( + if ( + homepage_elements is not None + and homepage_elements.filter( name=plugin_settings.PLUGIN_NAME, - ).exists(): - + ).exists() + ): if request.press.random_featured_journals: featured_journals = get_random_journals() else: @@ -35,16 +37,16 @@ def yield_homepage_element_context(request, homepage_elements): setting, c = press_models.PressSetting.objects.get_or_create( press=request.press, - name='journals_and_html_content', + name="journals_and_html_content", ) html_block_content = setting.value if not setting.value: - html_block_content = '

This element has no content.

' + html_block_content = "

This element has no content.

" return { - 'featured_journals': sorted(featured_journals, key=lambda x: x.name), - 'html_block_content': html_block_content, + "featured_journals": sorted(featured_journals, key=lambda x: x.name), + "html_block_content": html_block_content, } else: return {} diff --git a/src/core/homepage_elements/journals_and_html/plugin_settings.py b/src/core/homepage_elements/journals_and_html/plugin_settings.py index 73263e47f1..b1bd112a8d 100755 --- a/src/core/homepage_elements/journals_and_html/plugin_settings.py +++ b/src/core/homepage_elements/journals_and_html/plugin_settings.py @@ -8,8 +8,8 @@ from utils import models -PLUGIN_NAME = 'Journals and HTML' -DESCRIPTION = 'This plugin displays featured journals.' +PLUGIN_NAME = "Journals and HTML" +DESCRIPTION = "This plugin displays featured journals." VERSION = 1.0 @@ -23,12 +23,13 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='journals_and_html', - template_path='press/homepage_elements/journals_and_html.html', + configure_url="journals_and_html", + template_path="press/homepage_elements/journals_and_html.html", content_type=content_type, object_id=press.pk, has_config=True, - available_to_press=True) + available_to_press=True, + ) element.save() @@ -37,10 +38,10 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.journals_and_html.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.journals_and_html.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/journals_and_html/urls.py b/src/core/homepage_elements/journals_and_html/urls.py index 52b7c137cd..e7ccbcec04 100755 --- a/src/core/homepage_elements/journals_and_html/urls.py +++ b/src/core/homepage_elements/journals_and_html/urls.py @@ -8,5 +8,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.journals_and_html, name='journals_and_html'), + re_path(r"^$", views.journals_and_html, name="journals_and_html"), ] diff --git a/src/core/homepage_elements/journals_and_html/views.py b/src/core/homepage_elements/journals_and_html/views.py index 31ea982470..5ce0edce3e 100755 --- a/src/core/homepage_elements/journals_and_html/views.py +++ b/src/core/homepage_elements/journals_and_html/views.py @@ -15,12 +15,9 @@ def journals_and_html(request): html_setting, c = press_models.PressSetting.objects.get_or_create( press=request.press, - name='journals_and_html_content', - ) - form = forms.JournalsHTMLForm( - instance=request.press, - html_setting=html_setting + name="journals_and_html_content", ) + form = forms.JournalsHTMLForm(instance=request.press, html_setting=html_setting) if request.POST: form = forms.JournalsHTMLForm( @@ -32,23 +29,13 @@ def journals_and_html(request): form.save() form.save_m2m() - messages.add_message( - request, - messages.SUCCESS, - 'Saved.' - ) + messages.add_message(request, messages.SUCCESS, "Saved.") - return redirect( - reverse( - 'home_settings_index' - ) - ) + return redirect(reverse("home_settings_index")) - template = 'journals_and_html.html' + template = "journals_and_html.html" context = { - 'form': form, + "form": form, } return render(request, template, context) - - diff --git a/src/core/homepage_elements/news/forms.py b/src/core/homepage_elements/news/forms.py index 21264afea2..ce24dc3ab7 100644 --- a/src/core/homepage_elements/news/forms.py +++ b/src/core/homepage_elements/news/forms.py @@ -6,46 +6,45 @@ class NewsForm(forms.Form): number_of_articles = forms.IntegerField( - help_text='Number of news articles to display on the homepage.' + help_text="Number of news articles to display on the homepage." ) display_images = forms.BooleanField( - help_text='When enabled the News Plugin will display news images. ' - 'Note: this setting has no effect on the clean theme ' - 'which does not display images for news.', - required=False + help_text="When enabled the News Plugin will display news images. " + "Note: this setting has no effect on the clean theme " + "which does not display images for news.", + required=False, ) def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(NewsForm, self).__init__(*args, **kwargs) number_of_articles = setting_handler.get_setting( - 'plugin:News', - 'number_of_articles', + "plugin:News", + "number_of_articles", self.journal, ) display_images = setting_handler.get_setting( - 'plugin:News', - 'display_images', + "plugin:News", + "display_images", self.journal, ) - self.fields['number_of_articles'].initial = number_of_articles.value - self.fields['display_images'].initial = True if display_images.value else False + self.fields["number_of_articles"].initial = number_of_articles.value + self.fields["display_images"].initial = True if display_images.value else False def save(self, commit=True): - number_of_articles = self.cleaned_data.get('number_of_articles') - display_images = self.cleaned_data.get('display_images') + number_of_articles = self.cleaned_data.get("number_of_articles") + display_images = self.cleaned_data.get("display_images") if commit: setting_handler.save_setting( - 'plugin:News', - 'number_of_articles', + "plugin:News", + "number_of_articles", self.journal, number_of_articles, ) setting_handler.save_setting( - 'plugin:News', - 'display_images', + "plugin:News", + "display_images", self.journal, - 'On' if display_images else '', + "On" if display_images else "", ) - diff --git a/src/core/homepage_elements/news/hooks.py b/src/core/homepage_elements/news/hooks.py index bedc2ed0b4..062a6c0727 100755 --- a/src/core/homepage_elements/news/hooks.py +++ b/src/core/homepage_elements/news/hooks.py @@ -7,36 +7,35 @@ def yield_homepage_element_context(request, homepage_elements): from comms import models as comms_models from utils import models as utils_models - if homepage_elements is not None and homepage_elements.filter( - name='News').exists(): + if homepage_elements is not None and homepage_elements.filter(name="News").exists(): # If we only have a press and it has news items set, use them. if not request.journal and request.press.carousel_news_items.all(): - return {'news_items': request.press.carousel_news_items.all()} + return {"news_items": request.press.carousel_news_items.all()} - plugin = utils_models.Plugin.objects.get(name='News') + plugin = utils_models.Plugin.objects.get(name="News") number_of_articles = setting_handler.get_plugin_setting( - plugin, - 'number_of_articles', - request.journal if request.journal else None).value - number_of_articles = int( - number_of_articles) if number_of_articles else 2 + plugin, "number_of_articles", request.journal if request.journal else None + ).value + number_of_articles = int(number_of_articles) if number_of_articles else 2 display_images = setting_handler.get_setting( - 'plugin:News', - 'display_images', + "plugin:News", + "display_images", request.journal, ).value news_items = comms_models.NewsItem.objects.filter( - (Q(content_type=request.model_content_type) & Q( - object_id=request.site_type.id)) & - (Q(start_display__lte=timezone.now()) | Q(start_display=None)) & - (Q(end_display__gte=timezone.now()) | Q(end_display=None)) - ).order_by('-posted')[:number_of_articles] + ( + Q(content_type=request.model_content_type) + & Q(object_id=request.site_type.id) + ) + & (Q(start_display__lte=timezone.now()) | Q(start_display=None)) + & (Q(end_display__gte=timezone.now()) | Q(end_display=None)) + ).order_by("-posted")[:number_of_articles] return { - 'news_items': news_items, - 'display_images': display_images, + "news_items": news_items, + "display_images": display_images, } else: return {} diff --git a/src/core/homepage_elements/news/plugin_settings.py b/src/core/homepage_elements/news/plugin_settings.py index 0fe2277c09..04b88a3286 100755 --- a/src/core/homepage_elements/news/plugin_settings.py +++ b/src/core/homepage_elements/news/plugin_settings.py @@ -4,11 +4,11 @@ from utils import models, setting_handler -PLUGIN_NAME = 'News' -SHORT_NAME = 'news' -DESCRIPTION = 'This is a homepage element that renders News section.' -AUTHOR = 'Martin Paul Eve & Andy Byers' -VERSION = '1.0' +PLUGIN_NAME = "News" +SHORT_NAME = "news" +DESCRIPTION = "This is a homepage element that renders News section." +AUTHOR = "Martin Paul Eve & Andy Byers" +VERSION = "1.0" DEFAULT_NEWS = 5 # Defines how many news are to be displayed by default @@ -23,17 +23,17 @@ def install(): name=PLUGIN_NAME, version=VERSION, enabled=True, - display_name='News', + display_name="News", press_wide=True, homepage_element=True, ) - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=plugin.name) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=plugin.name) setting = setting_handler.create_setting( setting_group_name=plugin_group_name, - setting_name='number_of_articles', - type='number', - pretty_name='Number of Articles', - description='Number of news articles to display on the homepage.', + setting_name="number_of_articles", + type="number", + pretty_name="Number of Articles", + description="Number of news articles to display on the homepage.", is_translatable=False, ) setting_handler.get_or_create_default_setting( @@ -42,41 +42,42 @@ def install(): ) setting = setting_handler.create_setting( setting_group_name=plugin_group_name, - setting_name='display_images', - type='boolean', - pretty_name='Display Images', - description='When enabled the News Plugin will display news images.', + setting_name="display_images", + type="boolean", + pretty_name="Display Images", + description="When enabled the News Plugin will display news images.", is_translatable=False, ) setting_handler.get_or_create_default_setting( setting, - default_value='', + default_value="", ) for journal in journals: content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='news_config', - template_path='journal/homepage_elements/news.html', + configure_url="news_config", + template_path="journal/homepage_elements/news.html", content_type=content_type, object_id=journal.pk, has_config=True, - defaults={'available_to_press': True}) + defaults={"available_to_press": True}, + ) element.save() number_of_articles = setting_handler.get_plugin_setting( plugin=plugin, - setting_name='number_of_articles', + setting_name="number_of_articles", journal=journal, create=True, - pretty='Number of Articles', + pretty="Number of Articles", ) if number_of_articles in {None, " ", ""}: setting_handler.save_plugin_setting( plugin=plugin, - setting_name='number_of_articles', + setting_name="number_of_articles", value=DEFAULT_NEWS, journal=journal, ) @@ -87,12 +88,13 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='news_config', - template_path='journal/homepage_elements/news.html', + configure_url="news_config", + template_path="journal/homepage_elements/news.html", content_type=content_type, object_id=press.pk, has_config=True, - defaults={'available_to_press': True}) + defaults={"available_to_press": True}, + ) element.save() @@ -100,10 +102,10 @@ def install(): def hook_registry(): try: return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.news.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.news.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except (OperationalError, ProgrammingError): diff --git a/src/core/homepage_elements/news/urls.py b/src/core/homepage_elements/news/urls.py index c7286a4400..52d0625e50 100755 --- a/src/core/homepage_elements/news/urls.py +++ b/src/core/homepage_elements/news/urls.py @@ -3,5 +3,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.news_config, name='news_config'), + re_path(r"^$", views.news_config, name="news_config"), ] diff --git a/src/core/homepage_elements/news/views.py b/src/core/homepage_elements/news/views.py index f999a79cf3..2d71607bc8 100755 --- a/src/core/homepage_elements/news/views.py +++ b/src/core/homepage_elements/news/views.py @@ -17,16 +17,12 @@ def news_config(request): ) if form.is_valid(): form.save() - messages.add_message( - request, - messages.INFO, - 'News settings updated.' - ) - return redirect(reverse('home_settings_index')) + messages.add_message(request, messages.INFO, "News settings updated.") + return redirect(reverse("home_settings_index")) - template = 'news_settings.html' + template = "news_settings.html" context = { - 'form': form, + "form": form, } return render(request, template, context) diff --git a/src/core/homepage_elements/popular/forms.py b/src/core/homepage_elements/popular/forms.py index 6acf53db74..3e77ae9bb2 100644 --- a/src/core/homepage_elements/popular/forms.py +++ b/src/core/homepage_elements/popular/forms.py @@ -5,62 +5,60 @@ class FeaturedForm(forms.Form): - def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(FeaturedForm, self).__init__(*args, **kwargs) - most_popular, num_most_popular, most_popular_time = logic.get_popular_article_settings( - self.journal + most_popular, num_most_popular, most_popular_time = ( + logic.get_popular_article_settings(self.journal) ) - self.fields['num_most_popular'].initial = num_most_popular - self.fields['most_popular'].initial = most_popular - self.fields['most_popular_time'].initial = most_popular_time + self.fields["num_most_popular"].initial = num_most_popular + self.fields["most_popular"].initial = most_popular + self.fields["most_popular_time"].initial = most_popular_time most_popular = forms.BooleanField( - label='Display Most Popular Articles', - help_text='Displays the most popular articles.', + label="Display Most Popular Articles", + help_text="Displays the most popular articles.", required=False, ) num_most_popular = forms.IntegerField( - label='Number of Most Popular Articles to Display', - help_text='Determines how many popular articles we should display.', + label="Number of Most Popular Articles to Display", + help_text="Determines how many popular articles we should display.", ) most_popular_time = forms.ChoiceField( choices=( - ('weekly', 'Weekly'), - ('monthly', 'Monthly'), - ('yearly', 'Yearly'), + ("weekly", "Weekly"), + ("monthly", "Monthly"), + ("yearly", "Yearly"), ), - label='Number of Most Popular Articles to Display', - help_text='Determines how many popular articles we should display.', + label="Number of Most Popular Articles to Display", + help_text="Determines how many popular articles we should display.", ) def save(self, commit=True): - most_popular = self.cleaned_data.get('most_popular') - num_most_popular = self.cleaned_data.get('num_most_popular', 0) - most_popular_time = self.cleaned_data.get('most_popular_time') + most_popular = self.cleaned_data.get("most_popular") + num_most_popular = self.cleaned_data.get("num_most_popular", 0) + most_popular_time = self.cleaned_data.get("most_popular_time") if commit: setting_handler.save_plugin_setting( plugin_settings.get_self(), - 'most_popular', + "most_popular", most_popular, self.journal, ) setting_handler.save_plugin_setting( plugin_settings.get_self(), - 'num_most_popular', + "num_most_popular", num_most_popular, self.journal, ) setting_handler.save_plugin_setting( plugin_settings.get_self(), - 'most_popular_time', + "most_popular_time", most_popular_time, self.journal, ) - diff --git a/src/core/homepage_elements/popular/hooks.py b/src/core/homepage_elements/popular/hooks.py index 91cb543e0c..69cfc652ae 100755 --- a/src/core/homepage_elements/popular/hooks.py +++ b/src/core/homepage_elements/popular/hooks.py @@ -2,13 +2,13 @@ def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter( - name='Popular Articles', - ).exists(): - - most_popular, number, time = logic.get_popular_article_settings( - request.journal - ) + if ( + homepage_elements is not None + and homepage_elements.filter( + name="Popular Articles", + ).exists() + ): + most_popular, number, time = logic.get_popular_article_settings(request.journal) popular_articles = logic.get_most_popular_articles( request.journal, @@ -17,8 +17,8 @@ def yield_homepage_element_context(request, homepage_elements): ) return { - 'popular_articles': popular_articles, - 'most_popular': most_popular, + "popular_articles": popular_articles, + "most_popular": most_popular, } else: return {} diff --git a/src/core/homepage_elements/popular/logic.py b/src/core/homepage_elements/popular/logic.py index cec3e3c5fe..6639fac6f4 100644 --- a/src/core/homepage_elements/popular/logic.py +++ b/src/core/homepage_elements/popular/logic.py @@ -14,7 +14,7 @@ def get_popular_article_settings(journal): try: most_popular = setting_handler.get_plugin_setting( plugin, - 'most_popular', + "most_popular", journal, ).processed_value except AttributeError: @@ -23,7 +23,7 @@ def get_popular_article_settings(journal): try: num_most_popular = setting_handler.get_plugin_setting( plugin, - 'num_most_popular', + "num_most_popular", journal, ).processed_value except AttributeError: @@ -31,11 +31,11 @@ def get_popular_article_settings(journal): try: most_popular_time = setting_handler.get_plugin_setting( plugin, - 'most_popular_time', + "most_popular_time", journal, ).processed_value except AttributeError: - most_popular_time = 'weekly' + most_popular_time = "weekly" return most_popular, num_most_popular, most_popular_time @@ -43,9 +43,9 @@ def get_popular_article_settings(journal): def calc_start_date(time): date_time = timezone.now() - if time == 'weekly': + if time == "weekly": delta = 7 - elif time == 'monthly': + elif time == "monthly": delta = 30 else: delta = 365 @@ -56,12 +56,14 @@ def calc_start_date(time): def get_most_popular_articles(journal, number, time): start_date = calc_start_date(time) - articles = sm.Article.objects.filter( - journal=journal, - stage=sm.STAGE_PUBLISHED, - articleaccess__accessed__gte=start_date, - ).annotate( - access_count=Count("articleaccess") - ).order_by('-access_count', 'title')[:number] + articles = ( + sm.Article.objects.filter( + journal=journal, + stage=sm.STAGE_PUBLISHED, + articleaccess__accessed__gte=start_date, + ) + .annotate(access_count=Count("articleaccess")) + .order_by("-access_count", "title")[:number] + ) return articles diff --git a/src/core/homepage_elements/popular/plugin_settings.py b/src/core/homepage_elements/popular/plugin_settings.py index 67ab4b79cb..6bcdbc40db 100755 --- a/src/core/homepage_elements/popular/plugin_settings.py +++ b/src/core/homepage_elements/popular/plugin_settings.py @@ -6,11 +6,11 @@ logger = get_logger(__name__) -PLUGIN_NAME = 'Popular Articles' -SHORT_NAME = 'popular_articles' -DESCRIPTION = 'This is a homepage element that renders popular articles.' -AUTHOR = 'Martin Paul Eve' -VERSION = '1.0' +PLUGIN_NAME = "Popular Articles" +SHORT_NAME = "popular_articles" +DESCRIPTION = "This is a homepage element that renders popular articles." +AUTHOR = "Martin Paul Eve" +VERSION = "1.0" def get_self(): @@ -31,33 +31,33 @@ def install(): name=PLUGIN_NAME, version=VERSION, enabled=True, - display_name='Popular Articles', + display_name="Popular Articles", homepage_element=True, ) if c: - logger.debug('Plugin {} created'.format(PLUGIN_NAME)) + logger.debug("Plugin {} created".format(PLUGIN_NAME)) - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=PLUGIN_NAME) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=PLUGIN_NAME) setting = setting_handler.create_setting( setting_group_name=plugin_group_name, - setting_name='most_popular', - type='boolean', - pretty_name='Display Most Popular Articles', - description='Displays the most popular articles.', + setting_name="most_popular", + type="boolean", + pretty_name="Display Most Popular Articles", + description="Displays the most popular articles.", is_translatable=False, ) setting_handler.get_or_create_default_setting( setting=setting, - default_value='', + default_value="", ) setting = setting_handler.create_setting( setting_group_name=plugin_group_name, - setting_name='num_most_popular', - type='number', - pretty_name='Number of Most Popular Articles to Display', - description='Determines how many popular articles we should display.', + setting_name="num_most_popular", + type="number", + pretty_name="Number of Most Popular Articles to Display", + description="Determines how many popular articles we should display.", is_translatable=False, ) setting_handler.get_or_create_default_setting( @@ -66,15 +66,15 @@ def install(): ) setting = setting_handler.create_setting( setting_group_name=plugin_group_name, - setting_name='most_popular_time', - type='text', - pretty_name='Most Popular Timescale', - description='Select from this week, this month or this year.', + setting_name="most_popular_time", + type="text", + pretty_name="Most Popular Timescale", + description="Select from this week, this month or this year.", is_translatable=False, ) setting_handler.get_or_create_default_setting( setting=setting, - default_value='weekly', + default_value="weekly", ) # check whether this homepage element has already been installed for all journals @@ -84,8 +84,8 @@ def install(): content_type = ContentType.objects.get_for_model(journal) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='popular_articles_setup', - template_path='journal/homepage_elements/popular.html', + configure_url="popular_articles_setup", + template_path="journal/homepage_elements/popular.html", content_type=content_type, object_id=journal.pk, has_config=True, @@ -98,10 +98,10 @@ def hook_registry(): try: install() return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.popular.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.popular.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/popular/urls.py b/src/core/homepage_elements/popular/urls.py index 1198acad6e..5c589cf2e0 100755 --- a/src/core/homepage_elements/popular/urls.py +++ b/src/core/homepage_elements/popular/urls.py @@ -3,5 +3,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^manager/$', views.featured_articles, name='popular_articles_setup'), + re_path(r"^manager/$", views.featured_articles, name="popular_articles_setup"), ] diff --git a/src/core/homepage_elements/popular/views.py b/src/core/homepage_elements/popular/views.py index 9959900bb3..ff9bcb5fdf 100755 --- a/src/core/homepage_elements/popular/views.py +++ b/src/core/homepage_elements/popular/views.py @@ -12,8 +12,7 @@ def featured_articles(request): form = forms.FeaturedForm(journal=request.journal) if request.POST: - - if 'form' in request.POST: + if "form" in request.POST: form = forms.FeaturedForm(request.POST, journal=request.journal) if form.is_valid(): @@ -21,13 +20,13 @@ def featured_articles(request): messages.add_message( request, messages.SUCCESS, - 'Settings saved.', + "Settings saved.", ) - return redirect(reverse('popular_articles_setup')) + return redirect(reverse("popular_articles_setup")) - template = 'popular_setup.html' + template = "popular_setup.html" context = { - 'form': form, + "form": form, } return render(request, template, context) diff --git a/src/core/homepage_elements/preprints/forms.py b/src/core/homepage_elements/preprints/forms.py index ab76e911e4..f4dc997e82 100644 --- a/src/core/homepage_elements/preprints/forms.py +++ b/src/core/homepage_elements/preprints/forms.py @@ -8,10 +8,13 @@ class PreprintForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(PreprintForm, self).__init__(*args, **kwargs) - self.fields['homepage_preprints'].queryset = sub_models.Article.preprints.filter(date_published__isnull=False, - date_declined__isnull=True) - self.fields['homepage_preprints'].required = False + self.fields[ + "homepage_preprints" + ].queryset = sub_models.Article.preprints.filter( + date_published__isnull=False, date_declined__isnull=True + ) + self.fields["homepage_preprints"].required = False class Meta: model = models.Press - fields = ('homepage_preprints', 'random_homepage_preprints') + fields = ("homepage_preprints", "random_homepage_preprints") diff --git a/src/core/homepage_elements/preprints/hooks.py b/src/core/homepage_elements/preprints/hooks.py index c6accd1e04..c5f9b980bd 100755 --- a/src/core/homepage_elements/preprints/hooks.py +++ b/src/core/homepage_elements/preprints/hooks.py @@ -11,8 +11,10 @@ def get_random_preprints(): - preprints = submission_models.Article.preprints.filter(stage=submission_models.STAGE_PREPRINT_PUBLISHED, - date_published__lte=timezone.now()) + preprints = submission_models.Article.preprints.filter( + stage=submission_models.STAGE_PREPRINT_PUBLISHED, + date_published__lte=timezone.now(), + ) preprint_pks = [preprint.pk for preprint in preprints] random_preprints = list() random.shuffle(preprint_pks) @@ -26,11 +28,14 @@ def get_random_preprints(): def yield_homepage_element_context(request, homepage_elements): - if homepage_elements is not None and homepage_elements.filter(name='Preprints').exists(): + if ( + homepage_elements is not None + and homepage_elements.filter(name="Preprints").exists() + ): if request.press.random_homepage_preprints: - return {'preprints': get_random_preprints()} + return {"preprints": get_random_preprints()} else: print(request.press.homepage_preprints.all()) - return {'preprints': request.press.homepage_preprints.all()} + return {"preprints": request.press.homepage_preprints.all()} else: return {} diff --git a/src/core/homepage_elements/preprints/plugin_settings.py b/src/core/homepage_elements/preprints/plugin_settings.py index 62c272aa18..1e4ba6c93c 100755 --- a/src/core/homepage_elements/preprints/plugin_settings.py +++ b/src/core/homepage_elements/preprints/plugin_settings.py @@ -6,9 +6,9 @@ from django.db.utils import OperationalError from django.contrib.contenttypes.models import ContentType -PLUGIN_NAME = 'Preprints' -DESCRIPTION = 'This is a homepage element that renders a Preprint section.' -AUTHOR = 'Martin Paul Eve & Andy Byers' +PLUGIN_NAME = "Preprints" +DESCRIPTION = "This is a homepage element that renders a Preprint section." +AUTHOR = "Martin Paul Eve & Andy Byers" def install(): @@ -21,11 +21,12 @@ def install(): content_type = ContentType.objects.get_for_model(press) element, created = core_models.HomepageElement.objects.get_or_create( name=PLUGIN_NAME, - configure_url='preprints', - template_path='journal/homepage_elements/preprints.html', + configure_url="preprints", + template_path="journal/homepage_elements/preprints.html", content_type=content_type, object_id=press.pk, - has_config=True) + has_config=True, + ) element.save() @@ -33,10 +34,10 @@ def install(): def hook_registry(): try: return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.preprints.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.preprints.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except OperationalError: diff --git a/src/core/homepage_elements/preprints/urls.py b/src/core/homepage_elements/preprints/urls.py index 3d6e106f62..7b972f7897 100755 --- a/src/core/homepage_elements/preprints/urls.py +++ b/src/core/homepage_elements/preprints/urls.py @@ -8,5 +8,5 @@ urlpatterns = [ # Featured Articles - re_path(r'^$', views.preprints, name='preprints'), + re_path(r"^$", views.preprints, name="preprints"), ] diff --git a/src/core/homepage_elements/preprints/views.py b/src/core/homepage_elements/preprints/views.py index 51b2129a79..8f15a40ecb 100755 --- a/src/core/homepage_elements/preprints/views.py +++ b/src/core/homepage_elements/preprints/views.py @@ -12,7 +12,6 @@ @editor_user_required def preprints(request): - form = forms.PreprintForm(instance=request.press) if request.POST: @@ -20,11 +19,11 @@ def preprints(request): if form.is_valid(): form.save() - return redirect(reverse('preprints')) + return redirect(reverse("preprints")) - template = 'preprints.html' + template = "preprints.html" context = { - 'form': form, + "form": form, } return render(request, template, context) diff --git a/src/core/homepage_elements/search_bar/hooks.py b/src/core/homepage_elements/search_bar/hooks.py index fe06eb2ec5..3827b2d128 100755 --- a/src/core/homepage_elements/search_bar/hooks.py +++ b/src/core/homepage_elements/search_bar/hooks.py @@ -1,2 +1,2 @@ def yield_homepage_element_context(request, homepage_elements): - return {} + return {} diff --git a/src/core/homepage_elements/search_bar/plugin_settings.py b/src/core/homepage_elements/search_bar/plugin_settings.py index 1f908df6d6..49537b9fe6 100755 --- a/src/core/homepage_elements/search_bar/plugin_settings.py +++ b/src/core/homepage_elements/search_bar/plugin_settings.py @@ -4,11 +4,11 @@ from utils import models, setting_handler -PLUGIN_NAME = 'Search Bar' -SHORT_NAME = 'search_bar' -DESCRIPTION = 'This is a homepage element that renders a search bar.' -AUTHOR = 'Mauro Sanchez' -VERSION = '1.0' +PLUGIN_NAME = "Search Bar" +SHORT_NAME = "search_bar" +DESCRIPTION = "This is a homepage element that renders a search bar." +AUTHOR = "Mauro Sanchez" +VERSION = "1.0" def install(): @@ -23,10 +23,10 @@ def install(): defaults=dict( version=VERSION, enabled=True, - display_name='Search Bar', + display_name="Search Bar", press_wide=True, homepage_element=True, - ) + ), ) for journal in journals: content_type = ContentType.objects.get_for_model(journal) @@ -36,10 +36,10 @@ def install(): object_id=journal.pk, defaults=dict( configure_url=None, - template_path='core/homepage_elements/search_bar.html', + template_path="core/homepage_elements/search_bar.html", has_config=False, available_to_press=False, - ) + ), ) element.save() @@ -54,7 +54,7 @@ def install(): object_id=press.pk, defaults=dict( configure_url=None, - template_path='core/homepage_elements/search_bar.html', + template_path="core/homepage_elements/search_bar.html", has_config=False, available_to_press=False, ), @@ -66,10 +66,10 @@ def install(): def hook_registry(): try: return { - 'yield_homepage_element_context': { - 'module': 'core.homepage_elements.search_bar.hooks', - 'function': 'yield_homepage_element_context', - 'name': PLUGIN_NAME, + "yield_homepage_element_context": { + "module": "core.homepage_elements.search_bar.hooks", + "function": "yield_homepage_element_context", + "name": PLUGIN_NAME, } } except (OperationalError, ProgrammingError): diff --git a/src/core/homepage_elements/search_bar/urls.py b/src/core/homepage_elements/search_bar/urls.py index 412841d297..80c816d37f 100644 --- a/src/core/homepage_elements/search_bar/urls.py +++ b/src/core/homepage_elements/search_bar/urls.py @@ -3,5 +3,4 @@ __license__ = "AGPL v3" __maintainer__ = "Birkbeck Centre for Technology and Publishing" -urlpatterns = [ -] +urlpatterns = [] diff --git a/src/core/include_urls.py b/src/core/include_urls.py index dea31e2e0b..d0aec496eb 100644 --- a/src/core/include_urls.py +++ b/src/core/include_urls.py @@ -23,222 +23,365 @@ logger = get_logger(__name__) urlpatterns = [ - path('submit/', include('submission.urls')), - path('', include(journal_urls)), - path('review/', include('review.urls')), - path('metrics/', include('metrics.urls')), - path('identifiers/', include('identifiers.urls')), - path('production/', include('production.urls')), - path('proofing/', include('proofing.urls')), - path('cms/', include('cms.urls')), - path('transform/', include('transform.urls')), - path('copyediting/', include('copyediting.urls')), - path('rss/', include('rss.urls')), - path('feed/', include('rss.urls')), - path('cron/', include('cron.urls')), - path('install/', include('install.urls')), - path('i18n/', include('django.conf.urls.i18n')), - path('api/', include('api.urls')), - path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), - path('news/', include('comms.urls')), - path('reports/', include('reports.urls')), - path('repository/', include('repository.urls')), - path('utils/', include('utils.urls')), - path('workflow/', include('workflow.urls')), - path('discussion/', include('discussion.urls')), - path('oidc/', include('mozilla_django_oidc.urls')), - + path("submit/", include("submission.urls")), + path("", include(journal_urls)), + path("review/", include("review.urls")), + path("metrics/", include("metrics.urls")), + path("identifiers/", include("identifiers.urls")), + path("production/", include("production.urls")), + path("proofing/", include("proofing.urls")), + path("cms/", include("cms.urls")), + path("transform/", include("transform.urls")), + path("copyediting/", include("copyediting.urls")), + path("rss/", include("rss.urls")), + path("feed/", include("rss.urls")), + path("cron/", include("cron.urls")), + path("install/", include("install.urls")), + path("i18n/", include("django.conf.urls.i18n")), + path("api/", include("api.urls")), + path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), + path("news/", include("comms.urls")), + path("reports/", include("reports.urls")), + path("repository/", include("repository.urls")), + path("utils/", include("utils.urls")), + path("workflow/", include("workflow.urls")), + path("discussion/", include("discussion.urls")), + path("oidc/", include("mozilla_django_oidc.urls")), # Root Site URLS - re_path(r'^$', press_views.index, name='website_index'), - re_path(r'^journals/$', press_views.journals, name='press_journals'), - re_path(r'^conferences/$', press_views.conferences, name='press_conferences'), - re_path(r'^kanban/$', core_views.kanban, name='kanban'), - re_path(r'^login/$', core_views.user_login, name='core_login'), - re_path(r'^login/orcid/$', core_views.user_login_orcid, name='core_login_orcid'), - re_path(r'^register/step/1/$', core_views.register, name='core_register'), - re_path(r'^register/step/2/(?P[\w-]+)/$', core_views.activate_account, name='core_confirm_account'), - re_path(r'^register/step/orcid/(?P[\w-]+)/$', core_views.orcid_registration, name='core_orcid_registration'), - re_path(r'^reset/step/1/$', core_views.get_reset_token, name='core_get_reset_token'), - re_path(r'^reset/step/2/(?P[\w-]+)/$', core_views.reset_password, name='core_reset_password'), - re_path(r'^profile/$', core_views.edit_profile, name='core_edit_profile'), - re_path(r'^logout/$', core_views.user_logout, name='core_logout'), - re_path(r'^dashboard/$', core_views.dashboard, name='core_dashboard'), - re_path(r'^dashboard/active/$', core_views.active_submissions, name='core_active_submissions'), - re_path(r'^dashboard/active/filters/$', core_views.active_submission_filter, name='core_submission_filter'), - re_path(r'^dashboard/article/(?P\d+)/$', core_views.dashboard_article, name='core_dashboard_article'), - - re_path(r'^press/cover/$', press_views.serve_press_cover, name='press_cover_download'), - re_path(r'^press/file/(?P\d+)/$', + re_path(r"^$", press_views.index, name="website_index"), + re_path(r"^journals/$", press_views.journals, name="press_journals"), + re_path(r"^conferences/$", press_views.conferences, name="press_conferences"), + re_path(r"^kanban/$", core_views.kanban, name="kanban"), + re_path(r"^login/$", core_views.user_login, name="core_login"), + re_path(r"^login/orcid/$", core_views.user_login_orcid, name="core_login_orcid"), + re_path(r"^register/step/1/$", core_views.register, name="core_register"), + re_path( + r"^register/step/2/(?P[\w-]+)/$", + core_views.activate_account, + name="core_confirm_account", + ), + re_path( + r"^register/step/orcid/(?P[\w-]+)/$", + core_views.orcid_registration, + name="core_orcid_registration", + ), + re_path( + r"^reset/step/1/$", core_views.get_reset_token, name="core_get_reset_token" + ), + re_path( + r"^reset/step/2/(?P[\w-]+)/$", + core_views.reset_password, + name="core_reset_password", + ), + re_path(r"^profile/$", core_views.edit_profile, name="core_edit_profile"), + re_path(r"^logout/$", core_views.user_logout, name="core_logout"), + re_path(r"^dashboard/$", core_views.dashboard, name="core_dashboard"), + re_path( + r"^dashboard/active/$", + core_views.active_submissions, + name="core_active_submissions", + ), + re_path( + r"^dashboard/active/filters/$", + core_views.active_submission_filter, + name="core_submission_filter", + ), + re_path( + r"^dashboard/article/(?P\d+)/$", + core_views.dashboard_article, + name="core_dashboard_article", + ), + re_path( + r"^press/cover/$", press_views.serve_press_cover, name="press_cover_download" + ), + re_path( + r"^press/file/(?P\d+)/$", press_views.serve_press_file, - name='serve_press_file', - ), + name="serve_press_file", + ), re_path( - r'^press/user/all/$', + r"^press/user/all/$", press_views.AllUsers.as_view(), - name='press_all_users', + name="press_all_users", + ), + re_path(r"^press/merge_users/$", press_views.merge_users, name="merge_users"), + re_path( + r"^doi_manager/$", + press_views.IdentifierManager.as_view(), + name="press_identifier_manager", ), - re_path(r'^press/merge_users/$', press_views.merge_users, name='merge_users'), - re_path(r'^doi_manager/$', press_views.IdentifierManager.as_view(), name='press_identifier_manager'), - # Notes - re_path(r'^article/(?P\d+)/note/(?P\d+)/delete/$', core_views.delete_note, - name='kanban_delete_note'), - + re_path( + r"^article/(?P\d+)/note/(?P\d+)/delete/$", + core_views.delete_note, + name="kanban_delete_note", + ), # Manager URLS - re_path(r'^manager/$', core_views.manager_index, name='core_manager_index'), - + re_path(r"^manager/$", core_views.manager_index, name="core_manager_index"), # Settings Management - re_path(r'^manager/settings/$', core_views.settings_index, name='core_settings_index'), - re_path(r'^manager/default_settings/$', core_views.default_settings_index, name='core_default_settings_index'), - re_path(r'^manager/settings/group/(?P[-\w.: ]+)/setting/(?P[-\w.]+)/$', + re_path( + r"^manager/settings/$", core_views.settings_index, name="core_settings_index" + ), + re_path( + r"^manager/default_settings/$", + core_views.default_settings_index, + name="core_default_settings_index", + ), + re_path( + r"^manager/settings/group/(?P[-\w.: ]+)/setting/(?P[-\w.]+)/$", core_views.edit_setting, - name='core_edit_setting'), - re_path(r'^manager/settings/group/(?P[-\w.: ]+)/default_setting/(?P[-\w.]+)/$', + name="core_edit_setting", + ), + re_path( + r"^manager/settings/group/(?P[-\w.: ]+)/default_setting/(?P[-\w.]+)/$", core_views.edit_setting, - name='core_edit_default_setting'), - re_path(r'^manager/settings/(?P[-\w.]+)/$', core_views.edit_settings_group, name='core_edit_settings_group'), - re_path(r'^manager/settings/(?P[-\w.:]+)/(?P[-\w.]+)/(?P\d+)/$', - core_views.edit_plugin_settings_groups, name='core_edit_plugin_settings_groups'), - - re_path(r'^manager/home/settings/$', core_views.settings_home, name='home_settings_index'), - re_path(r'^manager/home/settings/order/$', core_views.journal_home_order, name='journal_home_order'), - + name="core_edit_default_setting", + ), + re_path( + r"^manager/settings/(?P[-\w.]+)/$", + core_views.edit_settings_group, + name="core_edit_settings_group", + ), + re_path( + r"^manager/settings/(?P[-\w.:]+)/(?P[-\w.]+)/(?P\d+)/$", + core_views.edit_plugin_settings_groups, + name="core_edit_plugin_settings_groups", + ), + re_path( + r"^manager/home/settings/$", + core_views.settings_home, + name="home_settings_index", + ), + re_path( + r"^manager/home/settings/order/$", + core_views.journal_home_order, + name="journal_home_order", + ), # Role Management - re_path(r'^manager/roles/$', core_views.roles, name='core_manager_roles'), - re_path(r'^manager/roles/(?P[-\w.]+)/$', core_views.role, name='core_manager_role'), - re_path(r'^manager/roles/(?P[-\w.]+)/user/(?P\d+)/(?P[-\w.]+)/$', core_views.role_action, - name='core_manager_role_action'), - + re_path(r"^manager/roles/$", core_views.roles, name="core_manager_roles"), + re_path( + r"^manager/roles/(?P[-\w.]+)/$", core_views.role, name="core_manager_role" + ), + re_path( + r"^manager/roles/(?P[-\w.]+)/user/(?P\d+)/(?P[-\w.]+)/$", + core_views.role_action, + name="core_manager_role_action", + ), # Users - re_path(r'^manager/user/$', core_views.users, name='core_manager_users'), - re_path(r'^manager/user/enrol/$', core_views.enrol_users, name='core_manager_enrol_users'), - re_path(r'^manager/user/inactive/$', core_views.inactive_users, name='core_manager_inactive_users'), - re_path(r'^manager/user/authenticated/$', core_views.logged_in_users, name='core_logged_in_users'), - re_path(r'^manager/user/add/$', core_views.add_user, name='core_add_user'), - re_path(r'^manager/user/(?P\d+)/edit/$', core_views.user_edit, name='core_user_edit'), - re_path(r'^manager/user/(?P\d+)/history/$', core_views.user_history, name='core_user_history'), - + re_path(r"^manager/user/$", core_views.users, name="core_manager_users"), + re_path( + r"^manager/user/enrol/$", + core_views.enrol_users, + name="core_manager_enrol_users", + ), + re_path( + r"^manager/user/inactive/$", + core_views.inactive_users, + name="core_manager_inactive_users", + ), + re_path( + r"^manager/user/authenticated/$", + core_views.logged_in_users, + name="core_logged_in_users", + ), + re_path(r"^manager/user/add/$", core_views.add_user, name="core_add_user"), + re_path( + r"^manager/user/(?P\d+)/edit/$", + core_views.user_edit, + name="core_user_edit", + ), + re_path( + r"^manager/user/(?P\d+)/history/$", + core_views.user_history, + name="core_user_history", + ), # Templates - re_path(r'^manager/templates/$', core_views.email_templates, name='core_email_templates'), - + re_path( + r"^manager/templates/$", core_views.email_templates, name="core_email_templates" + ), # Articles Images - re_path(r'^manager/article/images/$', core_views.article_images, name='core_article_images'), - re_path(r'^manager/article/images/edit/(?P\d+)/$', core_views.article_image_edit, - name='core_article_image_edit'), - + re_path( + r"^manager/article/images/$", + core_views.article_images, + name="core_article_images", + ), + re_path( + r"^manager/article/images/edit/(?P\d+)/$", + core_views.article_image_edit, + name="core_article_image_edit", + ), # Journal Contacts - re_path(r'^manager/contacts/$', core_views.contacts, name='core_journal_contacts'), - re_path(r'^manager/contacts/add/$', core_views.edit_contacts, name='core_new_journal_contact'), - re_path(r'^manager/contacts/(?P\d+)/$', core_views.edit_contacts, name='core_journal_contact'), - re_path(r'^manager/contacts/order/$', core_views.contacts_order, name='core_journal_contacts_order'), - + re_path(r"^manager/contacts/$", core_views.contacts, name="core_journal_contacts"), + re_path( + r"^manager/contacts/add/$", + core_views.edit_contacts, + name="core_new_journal_contact", + ), + re_path( + r"^manager/contacts/(?P\d+)/$", + core_views.edit_contacts, + name="core_journal_contact", + ), + re_path( + r"^manager/contacts/order/$", + core_views.contacts_order, + name="core_journal_contacts_order", + ), # Editorial Team - re_path(r'^manager/editorial/$', core_views.editorial_team, name='core_editorial_team'), - re_path(r'^manager/editorial/(?P\d+)/$', core_views.edit_editorial_group, - name='core_edit_editorial_team'), - re_path(r'^manager/editorial/new/$', core_views.edit_editorial_group, - name='core_add_editorial_team'), - re_path(r'^manager/editorial/(?P\d+)/add/$', core_views.add_member_to_group, - name='core_editorial_member_to_group'), - re_path(r'^manager/editorial/(?P\d+)/add/(?P\d+)/$', core_views.add_member_to_group, - name='core_editorial_member_to_group_user'), - re_path(r'^manager/editorial/order/(?P[-\w.]+)/$', + re_path( + r"^manager/editorial/$", core_views.editorial_team, name="core_editorial_team" + ), + re_path( + r"^manager/editorial/(?P\d+)/$", + core_views.edit_editorial_group, + name="core_edit_editorial_team", + ), + re_path( + r"^manager/editorial/new/$", + core_views.edit_editorial_group, + name="core_add_editorial_team", + ), + re_path( + r"^manager/editorial/(?P\d+)/add/$", + core_views.add_member_to_group, + name="core_editorial_member_to_group", + ), + re_path( + r"^manager/editorial/(?P\d+)/add/(?P\d+)/$", + core_views.add_member_to_group, + name="core_editorial_member_to_group_user", + ), + re_path( + r"^manager/editorial/order/(?P[-\w.]+)/$", core_views.editorial_ordering, - name='core_editorial_ordering'), - re_path(r'^manager/editorial/order/(?P[-\w.]+)/group/(?P\d+)/$', + name="core_editorial_ordering", + ), + re_path( + r"^manager/editorial/order/(?P[-\w.]+)/group/(?P\d+)/$", core_views.editorial_ordering, - name='core_editorial_ordering_group'), - + name="core_editorial_ordering_group", + ), # Notifications - re_path(r'^manager/notifications/$', - core_views.manage_notifications, name='core_manager_notifications'), - re_path(r'^manager/notifications/(?P\d+)/$', - core_views.manage_notifications, name='core_manager_edit_notifications'), - - # Plugin home re_path( - r'^manager/plugins/$', - core_views.plugin_list, - name='core_plugin_list' + r"^manager/notifications/$", + core_views.manage_notifications, + name="core_manager_notifications", ), re_path( - r'^plugins/$', - core_views.plugin_list, - name='core_plugin_list' + r"^manager/notifications/(?P\d+)/$", + core_views.manage_notifications, + name="core_manager_edit_notifications", ), - + # Plugin home + re_path(r"^manager/plugins/$", core_views.plugin_list, name="core_plugin_list"), + re_path(r"^plugins/$", core_views.plugin_list, name="core_plugin_list"), # Journal Sections - re_path(r'^manager/sections/$', - core_views.section_list, name='core_manager_sections'), - re_path(r'^manager/sections/add/$', - core_views.manage_section, name='core_manager_section_add'), - re_path(r'^manager/sections/(?P\d+)/$', - core_views.manage_section, name='core_manager_section'), - re_path(r'^manager/sections/(?P\d+)/articles/$', - core_views.section_articles, name='core_manager_section_articles'), - + re_path( + r"^manager/sections/$", core_views.section_list, name="core_manager_sections" + ), + re_path( + r"^manager/sections/add/$", + core_views.manage_section, + name="core_manager_section_add", + ), + re_path( + r"^manager/sections/(?P\d+)/$", + core_views.manage_section, + name="core_manager_section", + ), + re_path( + r"^manager/sections/(?P\d+)/articles/$", + core_views.section_articles, + name="core_manager_section_articles", + ), # Pinned Articles - re_path(r'^manager/articles/pinned/$', - core_views.pinned_articles, name='core_pinned_articles'), - + re_path( + r"^manager/articles/pinned/$", + core_views.pinned_articles, + name="core_pinned_articles", + ), # Press manager - re_path(r'^manager/press/$', - press_views.edit_press, - name='press_edit_press'), - re_path(r'^manager/press/journal_order/$', + re_path(r"^manager/press/$", press_views.edit_press, name="press_edit_press"), + re_path( + r"^manager/press/journal_order/$", press_views.journal_order, - name='press_journal_order'), - re_path(r'^manager/press/journal/(?P\d+)/domain/$', + name="press_journal_order", + ), + re_path( + r"^manager/press/journal/(?P\d+)/domain/$", press_views.journal_domain, - name='press_journal_domain'), - re_path(r'^manager/press/journal/(?P\d+)/description/$', + name="press_journal_domain", + ), + re_path( + r"^manager/press/journal/(?P\d+)/description/$", press_views.edit_press_journal_description, - name='edit_press_journal_description'), - + name="edit_press_journal_description", + ), # Workflow - re_path(r'^workflow/$', - core_views.journal_workflow, - name='core_journal_workflow'), - re_path(r'^workflow/order/$', + re_path(r"^workflow/$", core_views.journal_workflow, name="core_journal_workflow"), + re_path( + r"^workflow/order/$", core_views.order_workflow_elements, - name='core_order_workflow_elements'), - + name="core_order_workflow_elements", + ), # Cache - re_path(r'^manager/cache/flush/$', core_views.flush_cache, name='core_flush_cache'), - - re_path(r'^edit/article/(?P\d+)/metadata/$', submission_views.edit_metadata, name='edit_metadata'), - re_path(r'^edit/article/(?P\d+)/authors/order/$', submission_views.order_authors, name='order_authors'), - + re_path(r"^manager/cache/flush/$", core_views.flush_cache, name="core_flush_cache"), + re_path( + r"^edit/article/(?P\d+)/metadata/$", + submission_views.edit_metadata, + name="edit_metadata", + ), + re_path( + r"^edit/article/(?P\d+)/authors/order/$", + submission_views.order_authors, + name="order_authors", + ), # Public Profiles - re_path(r'profile/(?P[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/$', core_views.public_profile, name='core_public_profile'), - - re_path(r'^robots.txt$', press_views.robots, name='website_robots'), - re_path(r'^sitemap.xml$', press_views.sitemap, name='website_sitemap'), re_path( - r'^issue/(?P\d+)_sitemap.xml$', + r"profile/(?P[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/$", + core_views.public_profile, + name="core_public_profile", + ), + re_path(r"^robots.txt$", press_views.robots, name="website_robots"), + re_path(r"^sitemap.xml$", press_views.sitemap, name="website_sitemap"), + re_path( + r"^issue/(?P\d+)_sitemap.xml$", journal_views.sitemap, - name='journal_sitemap', + name="journal_sitemap", ), re_path( - r'^subject/(?P\d+)_sitemap.xml$', + r"^subject/(?P\d+)_sitemap.xml$", repository_views.sitemap, - name='repository_sitemap', + name="repository_sitemap", + ), + re_path( + r"^download/file/(?P\d+)/$", + journal_views.download_journal_file, + name="journal_file", + ), + re_path(r"^set-timezone/$", core_views.set_session_timezone, name="set_timezone"), + re_path( + r"^jsi18n/$", + cache_page(60 * 60, key_prefix="jsi18n_catalog")(JavaScriptCatalog.as_view()), + name="javascript-catalog", + ), + re_path( + r"permission/submit/$", + core_views.request_submission_access, + name="request_submission_access", + ), + re_path( + r"permission/requests/$", + core_views.manage_access_requests, + name="manage_access_requests", ), - - re_path(r'^download/file/(?P\d+)/$', journal_views.download_journal_file, name='journal_file'), - - re_path(r'^set-timezone/$', core_views.set_session_timezone, name='set_timezone'), - - re_path(r'^jsi18n/$', cache_page(60 * 60, key_prefix='jsi18n_catalog')(JavaScriptCatalog.as_view()), name='javascript-catalog'), - re_path(r'permission/submit/$', core_views.request_submission_access, name='request_submission_access'), - re_path(r'permission/requests/$', core_views.manage_access_requests, name='manage_access_requests'), ] # Journal homepage block loading blocks = plugin_loader.load( - os.path.join('core', 'homepage_elements'), - prefix='core.homepage_elements', + os.path.join("core", "homepage_elements"), + prefix="core.homepage_elements", permissive=True, ) @@ -246,15 +389,17 @@ for block in blocks: try: urlpatterns += [ - re_path(r'^homepage/elements/{0}/'.format(block.name), - include( - 'core.homepage_elements.{0}.urls'.format(block.name))), + re_path( + r"^homepage/elements/{0}/".format(block.name), + include("core.homepage_elements.{0}.urls".format(block.name)), + ), ] logger.debug("Loaded URLs for %s", block.name) except ImportError as error: logger.warning( "Failed to import urls for homepage element %s: %s", - block.name, error, + block.name, + error, ) except Exception as error: logger.error("Error loading homepage element %s", block.name) @@ -269,14 +414,16 @@ try: urlpatterns += [ re_path( - r'^plugins/{0}/'.format(plugin.best_name(slug=True)), - include('plugins.{0}.urls'.format(plugin.name)) + r"^plugins/{0}/".format(plugin.best_name(slug=True)), + include("plugins.{0}.urls".format(plugin.name)), ), ] logger.debug("Loaded URLs for %s", plugin.name) except ImportError as error: logger.warning( - "Failed to import urls for plugin %s: %s", plugin.name, error, + "Failed to import urls for plugin %s: %s", + plugin.name, + error, ) except Exception as error: logger.error("Error loading plugin %s", block.name) @@ -288,9 +435,9 @@ frameworks = [] for key, val in plugins.items(): - if hasattr(val, 'notify_hook'): + if hasattr(val, "notify_hook"): settings.NOTIFY_FUNCS.append(val.notify_hook) urlpatterns += [ - re_path(r'^site/(?P.*)/$', cms_views.view_page, name='cms_page'), + re_path(r"^site/(?P.*)/$", cms_views.view_page, name="cms_page"), ] diff --git a/src/core/janeway_global_settings.py b/src/core/janeway_global_settings.py index 28258c1d43..0f93b338e6 100755 --- a/src/core/janeway_global_settings.py +++ b/src/core/janeway_global_settings.py @@ -38,13 +38,13 @@ # SECURITY WARNING: keep the secret key used in production secret! # You should change this key before you go live! -SECRET_KEY = 'uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv' +SECRET_KEY = "uxprsdhk^gzd-r=_287byolxn)$k6tsd8_cepl^s^tms2w1qrv" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False COMMAND = sys.argv[1:] -IN_TEST_RUNNER = COMMAND[:1] == ['test'] -ALLOWED_HOSTS = ['*'] +IN_TEST_RUNNER = COMMAND[:1] == ["test"] +ALLOWED_HOSTS = ["*"] ENABLE_TEXTURE = False @@ -53,196 +53,193 @@ # Application definition INSTALLED_APPS = [ - 'modeltranslation', - 'apps.JanewayAdminConfig', - 'django.contrib.auth', - - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.humanize', - 'django.contrib.postgres', - 'django.contrib.contenttypes', - + "modeltranslation", + "apps.JanewayAdminConfig", + "django.contrib.auth", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.humanize", + "django.contrib.postgres", + "django.contrib.contenttypes", # Installed Apps # Install APP is loaded first to ensure all existing models and migrations # relying on installation procedures won't fail - 'install', - 'cms', - 'core', - 'copyediting', - 'cron', - 'discussion', - 'events', - 'identifiers', - 'journal', - 'metrics', - 'comms', - 'press', - 'production', - 'proofing', - 'review', - 'repository', - 'reports', - 'security', - 'submission', - 'transform', - 'utils', - 'workflow', - + "install", + "cms", + "core", + "copyediting", + "cron", + "discussion", + "events", + "identifiers", + "journal", + "metrics", + "comms", + "press", + "production", + "proofing", + "review", + "repository", + "reports", + "security", + "submission", + "transform", + "utils", + "workflow", # 3rd Party - 'mozilla_django_oidc', - 'django_summernote', - 'tinymce', - 'bootstrap4', - 'rest_framework', - 'foundationform', - 'materializecssform', - 'captcha', - 'simplemathcaptcha', - 'simple_history', - 'hijack', - 'hcaptcha', - 'django_bleach', - + "mozilla_django_oidc", + "django_summernote", + "tinymce", + "bootstrap4", + "rest_framework", + "foundationform", + "materializecssform", + "captcha", + "simplemathcaptcha", + "simple_history", + "hijack", + "hcaptcha", + "django_bleach", # Forms - 'django.forms', + "django.forms", ] INSTALLED_APPS += plugin_installed_apps.load_plugin_apps(BASE_DIR) INSTALLED_APPS += plugin_installed_apps.load_homepage_element_apps(BASE_DIR) MIDDLEWARE = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'core.middleware.TimezoneMiddleware', - 'core.middleware.SiteSettingsMiddleware', - 'core.middleware.MaintenanceModeMiddleware', - 'cron.middleware.CronMiddleware', - 'core.middleware.CounterCookieMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'core.middleware.PressMiddleware', - 'core.middleware.GlobalRequestMiddleware', - 'django.middleware.gzip.GZipMiddleware', - 'journal.middleware.LanguageMiddleware', - 'hijack.middleware.HijackUserMiddleware', - 'simple_history.middleware.HistoryRequestMiddleware', + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django.middleware.security.SecurityMiddleware", + "core.middleware.TimezoneMiddleware", + "core.middleware.SiteSettingsMiddleware", + "core.middleware.MaintenanceModeMiddleware", + "cron.middleware.CronMiddleware", + "core.middleware.CounterCookieMiddleware", + "django.middleware.locale.LocaleMiddleware", + "core.middleware.PressMiddleware", + "core.middleware.GlobalRequestMiddleware", + "django.middleware.gzip.GZipMiddleware", + "journal.middleware.LanguageMiddleware", + "hijack.middleware.HijackUserMiddleware", + "simple_history.middleware.HistoryRequestMiddleware", ) -ROOT_URLCONF = 'core.urls' +ROOT_URLCONF = "core.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': ([ - os.path.join(BASE_DIR, 'templates'), - os.path.join(BASE_DIR, 'templates', 'common'), - os.path.join(BASE_DIR, 'templates', 'admin'), - ] + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": ( + [ + os.path.join(BASE_DIR, "templates"), + os.path.join(BASE_DIR, "templates", "common"), + os.path.join(BASE_DIR, "templates", "admin"), + ] + plugin_installed_apps.load_plugin_templates(BASE_DIR) + plugin_installed_apps.load_homepage_element_templates(BASE_DIR) ), - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'core.context_processors.journal', - 'core.context_processors.journal_settings', - 'core.context_processors.press', - 'core.context_processors.active', - 'core.context_processors.navigation', - 'core.context_processors.version', - 'django_settings_export.settings_export', - 'django.template.context_processors.i18n' + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "core.context_processors.journal", + "core.context_processors.journal_settings", + "core.context_processors.press", + "core.context_processors.active", + "core.context_processors.navigation", + "core.context_processors.version", + "django_settings_export.settings_export", + "django.template.context_processors.i18n", ], - 'loaders': [ - 'utils.template_override_middleware.Loader', - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', + "loaders": [ + "utils.template_override_middleware.Loader", + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", + ], + "builtins": [ + "core.templatetags.fqdn", + "security.templatetags.securitytags", + "django.templatetags.i18n", ], - 'builtins': [ - 'core.templatetags.fqdn', - 'security.templatetags.securitytags', - 'django.templatetags.i18n', - ] }, }, ] -FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" SETTINGS_EXPORT = [ - 'ORCID_API_URL', - 'ORCID_TOKEN_URL', - 'ORCID_CLIENT_SECRET', - 'ORCID_CLIENT_ID', - 'ORCID_URL', - 'ENABLE_ENHANCED_MAILGUN_FEATURES', - 'ENABLE_ORCID', - 'DEBUG', - 'LANGUAGE_CODE', - 'URL_CONFIG', - 'HIJACK_USERS_ENABLED', - 'ENABLE_OIDC', - 'OIDC_SERVICE_NAME', + "ORCID_API_URL", + "ORCID_TOKEN_URL", + "ORCID_CLIENT_SECRET", + "ORCID_CLIENT_ID", + "ORCID_URL", + "ENABLE_ENHANCED_MAILGUN_FEATURES", + "ENABLE_ORCID", + "DEBUG", + "LANGUAGE_CODE", + "URL_CONFIG", + "HIJACK_USERS_ENABLED", + "ENABLE_OIDC", + "OIDC_SERVICE_NAME", ] -WSGI_APPLICATION = 'core.wsgi.application' -DEFAULT_HOST = 'https://www.example.org' # This is the default redirect if no other sites are found. +WSGI_APPLICATION = "core.wsgi.application" +DEFAULT_HOST = "https://www.example.org" # This is the default redirect if no other sites are found. # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases # We recommend mysql but Django supports PGSQL and SQLite amongst others if os.environ.get("DB_VENDOR") == "postgres": DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': os.environ["DB_NAME"], - 'USER': os.environ["DB_USER"], - 'PASSWORD': os.environ["DB_PASSWORD"], - 'HOST': os.environ["DB_HOST"], - 'PORT': os.environ["DB_PORT"], + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ["DB_NAME"], + "USER": os.environ["DB_USER"], + "PASSWORD": os.environ["DB_PASSWORD"], + "HOST": os.environ["DB_HOST"], + "PORT": os.environ["DB_PORT"], } } elif os.environ.get("DB_VENDOR") in {"mysql", "mariadb"}: DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': os.environ["DB_NAME"], - 'USER': os.environ["DB_USER"], - 'PASSWORD': os.environ["DB_PASSWORD"], - 'HOST': os.environ["DB_HOST"], - 'PORT': os.environ["DB_PORT"], - 'OPTIONS': { - 'init_command': 'SET default_storage_engine=INNODB; ' - 'ALTER DATABASE {0} CHARACTER SET utf8mb4 COLLATE ' - 'utf8mb4_general_ci'.format(os.environ["DB_NAME"]), - 'charset': 'utf8mb4', + "default": { + "ENGINE": "django.db.backends.mysql", + "NAME": os.environ["DB_NAME"], + "USER": os.environ["DB_USER"], + "PASSWORD": os.environ["DB_PASSWORD"], + "HOST": os.environ["DB_HOST"], + "PORT": os.environ["DB_PORT"], + "OPTIONS": { + "init_command": "SET default_storage_engine=INNODB; " + "ALTER DATABASE {0} CHARACTER SET utf8mb4 COLLATE " + "utf8mb4_general_ci".format(os.environ["DB_NAME"]), + "charset": "utf8mb4", }, } } else: DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '/db/janeway.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "/db/janeway.sqlite3", } } # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ -LANGUAGE_CODE = 'en' -TIME_ZONE = 'UTC' +LANGUAGE_CODE = "en" +TIME_ZONE = "UTC" LOCALE_PATHS = [ - os.path.join(BASE_DIR, 'core', 'locales') + os.path.join(BASE_DIR, "core", "locales") ] + plugin_installed_apps.load_plugin_locales(BASE_DIR) @@ -251,19 +248,19 @@ def gettext(s): LANGUAGES = ( - ('en', gettext('English')), - ('en-us', gettext('English (US)')), - ('fr', gettext('French')), - ('de', gettext('German')), - ('nl', gettext('Dutch')), - ('cy', gettext('Welsh')), + ("en", gettext("English")), + ("en-us", gettext("English (US)")), + ("fr", gettext("French")), + ("de", gettext("German")), + ("nl", gettext("Dutch")), + ("cy", gettext("Welsh")), ) -MODELTRANSLATION_DEFAULT_LANGUAGE = 'en' -MODELTRANSLATION_PREPOPULATE_LANGUAGE = 'en' +MODELTRANSLATION_DEFAULT_LANGUAGE = "en" +MODELTRANSLATION_PREPOPULATE_LANGUAGE = "en" -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, "media") +MEDIA_URL = "/media/" USE_I18N = True USE_L10N = False @@ -272,26 +269,40 @@ def gettext(s): # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ -STATIC_ROOT = os.path.join(BASE_DIR, 'collected-static') +STATIC_ROOT = os.path.join(BASE_DIR, "collected-static") STATICFILES_DIRS = ( # The /src/static/ folder is used by Janeway and should not be removed. - os.path.join(BASE_DIR, 'static'), + os.path.join(BASE_DIR, "static"), ) -STATIC_URL = '/static/' +STATIC_URL = "/static/" if ENABLE_TEXTURE: - STATICFILES_DIRS.append(os.path.join(BASE_DIR, 'texture')) + STATICFILES_DIRS.append(os.path.join(BASE_DIR, "texture")) # Django bleach settings BLEACH_ALLOWED_TAGS = get_allowed_html_tags() # Which HTML attributes are allowed BLEACH_ALLOWED_ATTRIBUTES = [ - "id", "class", "style", - "src", "href", # These are sanitized by scheme to avoid XSS - "alt", "title", "width", "height", "type", - "name", "value", "placeholder", "disabled", "readonly", - "required", "target", "checked", "selected" + "id", + "class", + "style", + "src", + "href", # These are sanitized by scheme to avoid XSS + "alt", + "title", + "width", + "height", + "type", + "name", + "value", + "placeholder", + "disabled", + "readonly", + "required", + "target", + "checked", + "selected", ] BLEACH_ALLOWED_PROTOCOLS = ["http", "https", "mailto"] @@ -309,125 +320,110 @@ def gettext(s): BLEACH_STRIP_COMMENTS = False # Which widget to use for bleached HTML fields -BLEACH_DEFAULT_WIDGET = 'tinymce.widgets.TinyMCE' +BLEACH_DEFAULT_WIDGET = "tinymce.widgets.TinyMCE" # Summernote settings SUMMERNOTE_CONFIG = { # Using SummernoteWidget - iframe mode - 'iframe': True, # or set False to use SummernoteInplaceWidget - no iframe mode - + "iframe": True, # or set False to use SummernoteInplaceWidget - no iframe mode # Use native HTML tags (``, ``, ...) instead of style attributes # (Firefox, Chrome only) - 'styleWithTags': True, - + "styleWithTags": True, # Set text direction : 'left to right' is default. - 'direction': 'ltr', - + "direction": "ltr", # Need authentication while uploading attachments. - 'attachment_require_authentication': True, - 'attachment_filesize_limit': 2056 * 2056, - - 'css': ( - '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.29.0/theme/monokai.min.css', + "attachment_require_authentication": True, + "attachment_filesize_limit": 2056 * 2056, + "css": ( + "//cdnjs.cloudflare.com/ajax/libs/codemirror/5.29.0/theme/monokai.min.css", ), - # You can put custom Summernote settings - 'summernote': { + "summernote": { # Using Summernote Air-mode - 'airMode': False, - + "airMode": False, # Change editor size - 'width': '100%', + "width": "100%", # 'height': '480', - # Toolbar customization # https://summernote.org/deep-dive/#custom-toolbar-popover - 'toolbar': [ - ['style', ['style']], - ['font', ['bold', 'italic', 'underline', 'clear']], + "toolbar": [ + ["style", ["style"]], + ["font", ["bold", "italic", "underline", "clear"]], # ['fontname', ['fontname']], # ['color', ['color']], - ['para', ['ul', 'ol']], # , 'paragraph' - ['table', ['table']], - ['insert', ['link', 'picture']], # , 'video' - ['misc', ['undo', 'redo', 'help']], - ['view', ['fullscreen', 'codeview']], + ["para", ["ul", "ol"]], # , 'paragraph' + ["table", ["table"]], + ["insert", ["link", "picture"]], # , 'video' + ["misc", ["undo", "redo", "help"]], + ["view", ["fullscreen", "codeview"]], ], - - 'popover': { - 'image': [ - ['remove', ['removeMedia']] - ], - 'link': [ - ['link', ['linkDialogShow', 'unlink']] - ], - 'table': [ - ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']], - ['delete', ['deleteRow', 'deleteCol', 'deleteTable']], - ], + "popover": { + "image": [["remove", ["removeMedia"]]], + "link": [["link", ["linkDialogShow", "unlink"]]], + "table": [ + ["add", ["addRowDown", "addRowUp", "addColLeft", "addColRight"]], + ["delete", ["deleteRow", "deleteCol", "deleteTable"]], + ], }, - - 'codemirror': { - 'mode': 'htmlmixed', - 'lineNumbers': 'true', - 'lineWrapping': 'true', + "codemirror": { + "mode": "htmlmixed", + "lineNumbers": "true", + "lineWrapping": "true", # You have to include theme file in 'css' or 'css_for_inplace' before using it. - 'theme': 'monokai', + "theme": "monokai", }, }, } # 1.9 appears confused about where null and blank are required for many to # many fields, so we're hiding these warning from the console -SILENCED_SYSTEM_CHECKS = ( - 'fields.W340', -) +SILENCED_SYSTEM_CHECKS = ("fields.W340",) LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'root': { - 'level': 'DEBUG' if DEBUG else 'INFO', - 'handlers': ['console', 'log_file'], + "version": 1, + "disable_existing_loggers": False, + "root": { + "level": "DEBUG" if DEBUG else "INFO", + "handlers": ["console", "log_file"], }, - 'formatters': { - 'default': { - 'format': '%(levelname)s %(asctime)s %(module)s ' - 'P:%(process)d T:%(thread)d %(message)s', + "formatters": { + "default": { + "format": "%(levelname)s %(asctime)s %(module)s " + "P:%(process)d T:%(thread)d %(message)s", }, - 'coloured': { - '()': 'colorlog.ColoredFormatter', - 'format': '%(log_color)s%(levelname)s %(asctime)s %(module)s ' - 'P:%(process)d T:%(thread)d %(message)s', - 'log_colors' : { - 'DEBUG': 'green', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'red', - } + "coloured": { + "()": "colorlog.ColoredFormatter", + "format": "%(log_color)s%(levelname)s %(asctime)s %(module)s " + "P:%(process)d T:%(thread)d %(message)s", + "log_colors": { + "DEBUG": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red", + }, }, }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'coloured', - 'stream': 'ext://sys.stdout', + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "coloured", + "stream": "ext://sys.stdout", }, - 'log_file': { - 'level': 'DEBUG', - 'class': 'logging.handlers.RotatingFileHandler', - 'maxBytes': 1024*1024*50, # 50 MB - 'backupCount': 1, - 'filename': os.path.join(PROJECT_DIR , 'logs/janeway.log'), - 'formatter': 'default' + "log_file": { + "level": "DEBUG", + "class": "logging.handlers.RotatingFileHandler", + "maxBytes": 1024 * 1024 * 50, # 50 MB + "backupCount": 1, + "filename": os.path.join(PROJECT_DIR, "logs/janeway.log"), + "formatter": "default", }, }, - 'loggers': { - 'django.db.backends': { - 'level': 'WARNING', - 'handlers': ['console', 'log_file'], - 'propagate': False, + "loggers": { + "django.db.backends": { + "level": "WARNING", + "handlers": ["console", "log_file"], + "propagate": False, }, }, } @@ -436,34 +432,35 @@ def gettext(s): class SuppressDeprecated(logging.Filter): def filter(self, record): WARNINGS_TO_SUPPRESS = [ - 'RemovedInDjango110Warning', + "RemovedInDjango110Warning", ] # Return false to suppress message. - return not any( - [warn in record.getMessage() for warn in WARNINGS_TO_SUPPRESS] - ) + return not any([warn in record.getMessage() for warn in WARNINGS_TO_SUPPRESS]) MESSAGE_TAGS = { - messages.ERROR: 'danger', + messages.ERROR: "danger", } -LOGIN_REDIRECT_URL = '/' -LOGIN_URL = '/login/' +LOGIN_REDIRECT_URL = "/" +LOGIN_URL = "/login/" -EMAIL_BACKEND = os.environ.get( - 'JANEWAY_EMAIL_BACKEND', -) or 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = os.environ.get("JANEWAY_EMAIL_HOST", '') -EMAIL_PORT = os.environ.get("JANEWAY_EMAIL_PORT", '') -EMAIL_HOST_USER = os.environ.get("JANEWAY_EMAIL_HOST_USER", '') -EMAIL_HOST_PASSWORD = os.environ.get("JANEWAY_EMAIL_HOST_PASSWORD", '') +EMAIL_BACKEND = ( + os.environ.get( + "JANEWAY_EMAIL_BACKEND", + ) + or "django.core.mail.backends.smtp.EmailBackend" +) +EMAIL_HOST = os.environ.get("JANEWAY_EMAIL_HOST", "") +EMAIL_PORT = os.environ.get("JANEWAY_EMAIL_PORT", "") +EMAIL_HOST_USER = os.environ.get("JANEWAY_EMAIL_HOST_USER", "") +EMAIL_HOST_PASSWORD = os.environ.get("JANEWAY_EMAIL_HOST_PASSWORD", "") EMAIL_USE_TLS = os.environ.get("JANEWAY_EMAIL_USE_TLS", True) DUMMY_EMAIL_DOMAIN = "@journal.com" # Settings for use with Mailgun -MAILGUN_ACCESS_KEY = '' -MAILGUN_SERVER_NAME = '' +MAILGUN_ACCESS_KEY = "" +MAILGUN_SERVER_NAME = "" MAILGUN_REQUIRE_TLS = False ENABLE_ENHANCED_MAILGUN_FEATURES = False # Enables email tracking @@ -471,44 +468,44 @@ def filter(self, record): DATE_FORMT = "Y-m-d" DATETIME_FORMAT = "Y-m-d H:i" -AUTH_USER_MODEL = 'core.Account' +AUTH_USER_MODEL = "core.Account" PLUGIN_HOOKS = {} NOTIFY_FUNCS = [] ENABLE_ORCID = True -ORCID_API_URL = 'http://pub.orcid.org/v1.2_rc7/' -ORCID_URL = 'https://orcid.org/oauth/authorize' -ORCID_TOKEN_URL = 'https://pub.orcid.org/oauth/token' -ORCID_CLIENT_SECRET = '' -ORCID_CLIENT_ID = '' - -SESSION_ENGINE = 'utils.sessions.janeway_db' -SESSION_COOKIE_NAME = 'JANEWAYSESSID' - -S3_ACCESS_KEY = '' -S3_SECRET_KEY = '' -S3_BUCKET_NAME = '' -END_POINT = 'eu-west-2' # eg. eu-west-1 -S3_HOST = 's3.eu-west-2.amazonaws.com' # eg. s3.eu-west-1.amazonaws.com - -BACKUP_TYPE = 'directory' # s3 or directory -BACKUP_DIR = '/path/to/backup/dir/' +ORCID_API_URL = "http://pub.orcid.org/v1.2_rc7/" +ORCID_URL = "https://orcid.org/oauth/authorize" +ORCID_TOKEN_URL = "https://pub.orcid.org/oauth/token" +ORCID_CLIENT_SECRET = "" +ORCID_CLIENT_ID = "" + +SESSION_ENGINE = "utils.sessions.janeway_db" +SESSION_COOKIE_NAME = "JANEWAYSESSID" + +S3_ACCESS_KEY = "" +S3_SECRET_KEY = "" +S3_BUCKET_NAME = "" +END_POINT = "eu-west-2" # eg. eu-west-1 +S3_HOST = "s3.eu-west-2.amazonaws.com" # eg. s3.eu-west-1.amazonaws.com + +BACKUP_TYPE = "directory" # s3 or directory +BACKUP_DIR = "/path/to/backup/dir/" BACKUP_EMAIL = False # If set to True, will send an email each time backup is run -URL_CONFIG = 'path' # path or domain +URL_CONFIG = "path" # path or domain # Captcha # You can get reCaptcha keys for your domain here: https://developers.google.com/recaptcha/intro # You can set either to use Google's reCaptcha or a basic math field with no external requirements -CAPTCHA_TYPE = 'simple_math' # should be either 'simple_math' or 'recaptcha' to enable captcha fields otherwise disabled -RECAPTCHA_PRIVATE_KEY = '' # Public and private keys are required when using recaptcha -RECAPTCHA_PUBLIC_KEY = '' +CAPTCHA_TYPE = "simple_math" # should be either 'simple_math' or 'recaptcha' to enable captcha fields otherwise disabled +RECAPTCHA_PRIVATE_KEY = "" # Public and private keys are required when using recaptcha +RECAPTCHA_PUBLIC_KEY = "" BOOTSTRAP4 = { - 'required_css_class': 'required', + "required_css_class": "required", } SILENT_IMPORT_CACHE = True @@ -522,19 +519,15 @@ def filter(self, record): # New XML galleys will be associated with this stylesheet by default when they # are first uploaded -DEFAULT_XSL_FILE_LABEL = 'Janeway default (1.6.0)' +DEFAULT_XSL_FILE_LABEL = "Janeway default (1.6.0)" # Skip migrations by default on sqlite for faster execution -if ( - IN_TEST_RUNNER - and "--keepdb" not in COMMAND -): +if IN_TEST_RUNNER and "--keepdb" not in COMMAND: from collections.abc import Mapping - class SkipMigrations(Mapping): def __getitem__(self, key): - """ Ensures the install migrations run before syncing db + """Ensures the install migrations run before syncing db Django's migration executor will always pre_render database state from the models of unmigrated apps before running those declared in @@ -542,8 +535,9 @@ def __getitem__(self, key): first, while skipping the remaining migrations. Instead, we run the required SQL here. """ - if key == 'install': + if key == "install": from django.db import connection + if connection.vendor == "postgresql": cursor = connection.cursor() cursor.execute("CREATE EXTENSION IF NOT EXISTS citext;") @@ -566,29 +560,29 @@ def __len__(self): # A potentially dangerous feature, this allows superusers to hijack and control a user's account. HIJACK_USERS_ENABLED = False -HIJACK_LOGIN_REDIRECT_URL = '/manager/' +HIJACK_LOGIN_REDIRECT_URL = "/manager/" # OIDC -ENABLE_OIDC = bool(os.environ.get('ENABLE_OIDC', False)) -OIDC_SERVICE_NAME = 'OIDC Service Name' -OIDC_CALLBACK_CLASS = 'utils.oidc.JanewayOIDCAuthenticationCallbackView' -OIDC_OP_LOGOUT_URL_METHOD = 'utils.oidc.logout_url' -OIDC_USERNAME_ALGO = 'utils.oidc.generate_oidc_username' -OIDC_LOGOUT_URL = os.environ.get('OIDC_LOGOUT_URL') -OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID') -OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET') -OIDC_RP_SIGN_ALGO = os.environ.get('OIDC_RP_SIGN_ALGO') -OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT') -OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT') -OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT') -OIDC_OP_JWKS_ENDPOINT = os.environ.get('OIDC_OP_JWKS_ENDPOINT') +ENABLE_OIDC = bool(os.environ.get("ENABLE_OIDC", False)) +OIDC_SERVICE_NAME = "OIDC Service Name" +OIDC_CALLBACK_CLASS = "utils.oidc.JanewayOIDCAuthenticationCallbackView" +OIDC_OP_LOGOUT_URL_METHOD = "utils.oidc.logout_url" +OIDC_USERNAME_ALGO = "utils.oidc.generate_oidc_username" +OIDC_LOGOUT_URL = os.environ.get("OIDC_LOGOUT_URL") +OIDC_RP_CLIENT_ID = os.environ.get("OIDC_RP_CLIENT_ID") +OIDC_RP_CLIENT_SECRET = os.environ.get("OIDC_RP_CLIENT_SECRET") +OIDC_RP_SIGN_ALGO = os.environ.get("OIDC_RP_SIGN_ALGO") +OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get("OIDC_OP_AUTHORIZATION_ENDPOINT") +OIDC_OP_TOKEN_ENDPOINT = os.environ.get("OIDC_OP_TOKEN_ENDPOINT") +OIDC_OP_USER_ENDPOINT = os.environ.get("OIDC_OP_USER_ENDPOINT") +OIDC_OP_JWKS_ENDPOINT = os.environ.get("OIDC_OP_JWKS_ENDPOINT") if ENABLE_OIDC: AUTHENTICATION_BACKENDS = ( - 'mozilla_django_oidc.auth.OIDCAuthenticationBackend', - 'django.contrib.auth.backends.ModelBackend', + "mozilla_django_oidc.auth.OIDCAuthenticationBackend", + "django.contrib.auth.backends.ModelBackend", ) CORE_FILETEXT_MODEL = "core.FileText" @@ -602,30 +596,30 @@ def __len__(self): # Expects a tuple or None. Tuple examples: (23, 'daily') # (12, 'hourly') (30, 'mins') SITE_SEARCH_INDEXING_FREQUENCY = None -SITE_SEARCH_DIR = 'site_search_test' if IN_TEST_RUNNER else 'site_search' +SITE_SEARCH_DIR = "site_search_test" if IN_TEST_RUNNER else "site_search" # A core theme must include ALL templates. CORE_THEMES = [ - 'OLH', - 'material', - 'clean', + "OLH", + "material", + "clean", ] # Repository theme setting determines which themes currently # support repositories. REPOSITORY_THEMES = [ - 'OLH', - 'material', + "OLH", + "material", ] -INSTALLATION_BASE_THEME = 'OLH' +INSTALLATION_BASE_THEME = "OLH" -DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" # Use pagination for all of our APIs based on Django REST Framework REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', - 'PAGE_SIZE': 100, + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", + "PAGE_SIZE": 100, } TINYMCE_CLIPBOARD_CLEANER = { # Settings required to optionally clean formatting from a paste event @@ -669,13 +663,13 @@ def __len__(self): "menubar": "edit view insert format tools table help", "content_css": STATIC_URL + "/admin/css/admin.css", "plugins": "advlist autolink lists link image charmap preview anchor searchreplace visualblocks code" - " fullscreen insertdatetime media table code help wordcount spellchecker help", + " fullscreen insertdatetime media table code help wordcount spellchecker help", "toolbar": "help removeformat | undo redo | bold italic underline strikethrough " - "| fontsizeselect formatselect " - "| outdent indent | formatselect | numlist bullist checklist " - "| forecolor backcolor permanentpen formatpainter | pagebreak " - "| charmap emoticons " - "| fullscreen | image media template link anchor codesample " - "| a11ycheck ltr rtl | code", + "| fontsizeselect formatselect " + "| outdent indent | formatselect | numlist bullist checklist " + "| forecolor backcolor permanentpen formatpainter | pagebreak " + "| charmap emoticons " + "| fullscreen | image media template link anchor codesample " + "| a11ycheck ltr rtl | code", **TINYMCE_CLIPBOARD_CLEANER, } diff --git a/src/core/logic.py b/src/core/logic.py index 98268f3ae4..848acd201c 100755 --- a/src/core/logic.py +++ b/src/core/logic.py @@ -38,15 +38,15 @@ def send_reset_token(request, reset_token): core_reset_password_url = request.site_type.site_url( reverse( - 'core_reset_password', - kwargs={'token': reset_token.token}, + "core_reset_password", + kwargs={"token": reset_token.token}, ) ) context = { - 'reset_token': reset_token, - 'core_reset_password_url': core_reset_password_url, + "reset_token": reset_token, + "core_reset_password_url": core_reset_password_url, } - log_dict = {'level': 'Info', 'types': 'Reset Token', 'target': None} + log_dict = {"level": "Info", "types": "Reset Token", "target": None} if not request.journal: message = render_template.get_message_content( request, @@ -58,10 +58,10 @@ def send_reset_token(request, reset_token): message = render_template.get_message_content( request, context, - 'password_reset', + "password_reset", ) - subject = 'subject_password_reset' + subject = "subject_password_reset" notify_helpers.send_email_with_body_from_user( request, @@ -75,13 +75,13 @@ def send_reset_token(request, reset_token): def send_confirmation_link(request, new_user): core_confirm_account_url = request.site_type.site_url( reverse( - 'core_confirm_account', - kwargs={'token': new_user.confirmation_code}, + "core_confirm_account", + kwargs={"token": new_user.confirmation_code}, ) ) context = { - 'user': new_user, - 'core_confirm_account_url': core_confirm_account_url, + "user": new_user, + "core_confirm_account_url": core_confirm_account_url, } if not request.journal: message = render_template.get_message_content( @@ -94,17 +94,17 @@ def send_confirmation_link(request, new_user): message = render_template.get_message_content( request, context, - 'new_user_registration', + "new_user_registration", ) - subject = 'subject_new_user_registration' + subject = "subject_new_user_registration" notify_helpers.send_slack( request, - 'New registration: {0}'.format(new_user.full_name()), - ['slack_admins'], + "New registration: {0}".format(new_user.full_name()), + ["slack_admins"], ) - log_dict = {'level': 'Info', 'types': 'Account Confirmation', 'target': None} + log_dict = {"level": "Info", "types": "Account Confirmation", "target": None} notify_helpers.send_email_with_body_from_user( request, subject, @@ -114,7 +114,7 @@ def send_confirmation_link(request, new_user): ) -def resize_and_crop(img_path, size, crop_type='middle'): +def resize_and_crop(img_path, size, crop_type="middle"): """ Resize and crop an image to fit the specified size. """ @@ -139,14 +139,19 @@ def resize_and_crop(img_path, size, crop_type='middle'): Image.LANCZOS, ) # Crop in the top, middle or bottom - if crop_type == 'top': + if crop_type == "top": box = (0, 0, img.size[0], size[1]) - elif crop_type == 'middle': - box = (0, (img.size[1] - size[1]) // 2, img.size[0], (img.size[1] + size[1]) // 2) - elif crop_type == 'bottom': + elif crop_type == "middle": + box = ( + 0, + (img.size[1] - size[1]) // 2, + img.size[0], + (img.size[1] + size[1]) // 2, + ) + elif crop_type == "bottom": box = (0, img.size[1] - size[1], img.size[0], img.size[1]) else: - raise ValueError('ERROR: invalid value for crop_type') + raise ValueError("ERROR: invalid value for crop_type") img = img.crop(box) elif ratio < img_ratio: @@ -155,23 +160,25 @@ def resize_and_crop(img_path, size, crop_type='middle'): Image.LANCZOS, ) # Crop in the top, middle or bottom - if crop_type == 'top': + if crop_type == "top": box = (0, 0, size[0], img.size[1]) - elif crop_type == 'middle': + elif crop_type == "middle": horizontal_padding = (size[0] - img.size[0]) // 2 vertical_padding = (size[1] - img.size[1]) // 2 offset_tuple = (horizontal_padding, vertical_padding) - final_thumb = Image.new(mode='RGBA', size=size, color=(255, 255, 255, 0)) - final_thumb.paste(img, offset_tuple) # paste the thumbnail into the full sized image + final_thumb = Image.new(mode="RGBA", size=size, color=(255, 255, 255, 0)) + final_thumb.paste( + img, offset_tuple + ) # paste the thumbnail into the full sized image final_thumb.save(img_path, "png") return - elif crop_type == 'bottom': + elif crop_type == "bottom": box = (img.size[0] - size[0], 0, img.size[0], img.size[1]) else: - raise ValueError('ERROR: invalid value for crop_type') + raise ValueError("ERROR: invalid value for crop_type") img = img.crop(box) else: @@ -192,7 +199,7 @@ def settings_for_context(request): @cache(600) def cached_settings_for_context(journal, language): - setting_groups = ['general', 'crosscheck', 'article', 'news', 'styling'] + setting_groups = ["general", "crosscheck", "article", "news", "styling"] _dict = {group: {} for group in setting_groups} for group in setting_groups: @@ -210,10 +217,12 @@ def cached_settings_for_context(journal, language): def process_setting_list(settings_to_get, type, journal): settings = [] for setting in settings_to_get: - settings.append({ - 'name': setting, - 'object': setting_handler.get_setting(type, setting, journal), - }) + settings.append( + { + "name": setting, + "object": setting_handler.get_setting(type, setting, journal), + } + ) return settings @@ -226,321 +235,444 @@ def get_settings_to_edit(display_group, journal, user): ): review_form_choices.append([form.pk, form]) - if display_group == 'submission': + if display_group == "submission": group_of_settings = [ - {'name': 'disable_journal_submission', - 'object': setting_handler.get_setting('general', 'disable_journal_submission', journal) - }, - {'name': 'disable_journal_submission_message', - 'object': setting_handler.get_setting('general', 'disable_journal_submission_message', journal) - }, - {'name': 'limit_access_to_submission', - 'object': setting_handler.get_setting('general', 'limit_access_to_submission', journal) - }, - {'name': 'submission_access_request_text', - 'object': setting_handler.get_setting('general', 'submission_access_request_text', journal) - }, - {'name': 'submission_access_request_contact', - 'object': setting_handler.get_setting('general', 'submission_access_request_contact', journal) - }, - {'name': 'abstract_required', - 'object': setting_handler.get_setting( - 'general', - 'abstract_required', - journal, - ) - }, - {'name': 'submission_intro_text', - 'object': setting_handler.get_setting( - 'general', - 'submission_intro_text', - journal - ) - }, - {'name': 'copyright_notice', - 'object': setting_handler.get_setting('general', 'copyright_notice', journal) - }, - {'name': 'submission_checklist', - 'object': setting_handler.get_setting('general', 'submission_checklist', journal) - }, - {'name': 'acceptance_criteria', - 'object': setting_handler.get_setting('general', 'acceptance_criteria', journal) - }, - {'name': 'publication_fees', - 'object': setting_handler.get_setting('general', 'publication_fees', journal) - }, - {'name': 'editors_for_notification', - 'object': setting_handler.get_setting('general', 'editors_for_notification', journal), - 'choices': journal.editor_pks() - }, - {'name': 'user_automatically_author', - 'object': setting_handler.get_setting('general', 'user_automatically_author', journal), - }, - {'name': 'submission_summary', - 'object': setting_handler.get_setting('general', 'submission_summary', journal), - }, - {'name': 'limit_manuscript_types', - 'object': setting_handler.get_setting('general', 'limit_manuscript_types', journal), - }, - {'name': 'accepts_preprint_submissions', - 'object': setting_handler.get_setting('general', 'accepts_preprint_submissions', journal), - }, - {'name': 'focus_and_scope', - 'object': setting_handler.get_setting('general', 'focus_and_scope', journal), - }, - {'name': 'publication_cycle', - 'object': setting_handler.get_setting('general', 'publication_cycle', journal), - }, - {'name': 'peer_review_info', - 'object': setting_handler.get_setting('general', 'peer_review_info', journal), - }, - {'name': 'copyright_submission_label', - 'object': setting_handler.get_setting('general', 'copyright_submission_label', journal) - }, - { - 'name': 'file_submission_guidelines', - 'object': setting_handler.get_setting( - 'general', - 'file_submission_guidelines', journal - ), - }, - { - 'name': 'manuscript_file_submission_instructions', - 'object': setting_handler.get_setting( - 'general', - 'manuscript_file_submission_instructions', journal - ), - }, - { - 'name': 'data_figure_file_submission_instructions', - 'object': setting_handler.get_setting( - 'general', - 'data_figure_file_submission_instructions', journal - ), - }, - { - 'name': 'hide_editors_from_authors', - 'object': setting_handler.get_setting( - 'general', - 'hide_editors_from_authors', journal + { + "name": "disable_journal_submission", + "object": setting_handler.get_setting( + "general", "disable_journal_submission", journal ), - } + }, + { + "name": "disable_journal_submission_message", + "object": setting_handler.get_setting( + "general", "disable_journal_submission_message", journal + ), + }, + { + "name": "limit_access_to_submission", + "object": setting_handler.get_setting( + "general", "limit_access_to_submission", journal + ), + }, + { + "name": "submission_access_request_text", + "object": setting_handler.get_setting( + "general", "submission_access_request_text", journal + ), + }, + { + "name": "submission_access_request_contact", + "object": setting_handler.get_setting( + "general", "submission_access_request_contact", journal + ), + }, + { + "name": "abstract_required", + "object": setting_handler.get_setting( + "general", + "abstract_required", + journal, + ), + }, + { + "name": "submission_intro_text", + "object": setting_handler.get_setting( + "general", "submission_intro_text", journal + ), + }, + { + "name": "copyright_notice", + "object": setting_handler.get_setting( + "general", "copyright_notice", journal + ), + }, + { + "name": "submission_checklist", + "object": setting_handler.get_setting( + "general", "submission_checklist", journal + ), + }, + { + "name": "acceptance_criteria", + "object": setting_handler.get_setting( + "general", "acceptance_criteria", journal + ), + }, + { + "name": "publication_fees", + "object": setting_handler.get_setting( + "general", "publication_fees", journal + ), + }, + { + "name": "editors_for_notification", + "object": setting_handler.get_setting( + "general", "editors_for_notification", journal + ), + "choices": journal.editor_pks(), + }, + { + "name": "user_automatically_author", + "object": setting_handler.get_setting( + "general", "user_automatically_author", journal + ), + }, + { + "name": "submission_summary", + "object": setting_handler.get_setting( + "general", "submission_summary", journal + ), + }, + { + "name": "limit_manuscript_types", + "object": setting_handler.get_setting( + "general", "limit_manuscript_types", journal + ), + }, + { + "name": "accepts_preprint_submissions", + "object": setting_handler.get_setting( + "general", "accepts_preprint_submissions", journal + ), + }, + { + "name": "focus_and_scope", + "object": setting_handler.get_setting( + "general", "focus_and_scope", journal + ), + }, + { + "name": "publication_cycle", + "object": setting_handler.get_setting( + "general", "publication_cycle", journal + ), + }, + { + "name": "peer_review_info", + "object": setting_handler.get_setting( + "general", "peer_review_info", journal + ), + }, + { + "name": "copyright_submission_label", + "object": setting_handler.get_setting( + "general", "copyright_submission_label", journal + ), + }, + { + "name": "file_submission_guidelines", + "object": setting_handler.get_setting( + "general", "file_submission_guidelines", journal + ), + }, + { + "name": "manuscript_file_submission_instructions", + "object": setting_handler.get_setting( + "general", "manuscript_file_submission_instructions", journal + ), + }, + { + "name": "data_figure_file_submission_instructions", + "object": setting_handler.get_setting( + "general", "data_figure_file_submission_instructions", journal + ), + }, + { + "name": "hide_editors_from_authors", + "object": setting_handler.get_setting( + "general", "hide_editors_from_authors", journal + ), + }, ] - setting_group = 'general' + setting_group = "general" - elif display_group == 'review': + elif display_group == "review": group_of_settings = [ { - 'name': 'reviewer_guidelines', - 'object': setting_handler.get_setting('general', 'reviewer_guidelines', journal), + "name": "reviewer_guidelines", + "object": setting_handler.get_setting( + "general", "reviewer_guidelines", journal + ), }, { - 'name': 'default_review_visibility', - 'object': setting_handler.get_setting('general', 'default_review_visibility', journal), - 'choices': review_models.review_visibilty() + "name": "default_review_visibility", + "object": setting_handler.get_setting( + "general", "default_review_visibility", journal + ), + "choices": review_models.review_visibilty(), }, { - 'name': 'review_file_help', - 'object': setting_handler.get_setting('general', 'review_file_help', journal), + "name": "review_file_help", + "object": setting_handler.get_setting( + "general", "review_file_help", journal + ), }, { - 'name': 'default_review_days', - 'object': setting_handler.get_setting('general', 'default_review_days', journal), + "name": "default_review_days", + "object": setting_handler.get_setting( + "general", "default_review_days", journal + ), }, { - 'name': 'enable_save_review_progress', - 'object': setting_handler.get_setting('general', 'enable_save_review_progress', journal), + "name": "enable_save_review_progress", + "object": setting_handler.get_setting( + "general", "enable_save_review_progress", journal + ), }, { - 'name': 'enable_one_click_access', - 'object': setting_handler.get_setting('general', 'enable_one_click_access', journal), + "name": "enable_one_click_access", + "object": setting_handler.get_setting( + "general", "enable_one_click_access", journal + ), }, { - 'name': 'enable_expanded_review_details', - 'object': setting_handler.get_setting('general', 'enable_expanded_review_details', journal), + "name": "enable_expanded_review_details", + "object": setting_handler.get_setting( + "general", "enable_expanded_review_details", journal + ), }, { - 'name': 'draft_decisions', - 'object': setting_handler.get_setting('general', 'draft_decisions', journal), + "name": "draft_decisions", + "object": setting_handler.get_setting( + "general", "draft_decisions", journal + ), }, { - 'name': 'default_review_form', - 'object': setting_handler.get_setting('general', 'default_review_form', journal), - 'choices': review_form_choices + "name": "default_review_form", + "object": setting_handler.get_setting( + "general", "default_review_form", journal + ), + "choices": review_form_choices, }, { - 'name': 'reviewer_form_download', - 'object': setting_handler.get_setting('general', 'reviewer_form_download', journal), + "name": "reviewer_form_download", + "object": setting_handler.get_setting( + "general", "reviewer_form_download", journal + ), }, { - 'name': 'peer_review_upload_text', - 'object': setting_handler.get_setting('general', 'peer_review_upload_text', journal), + "name": "peer_review_upload_text", + "object": setting_handler.get_setting( + "general", "peer_review_upload_text", journal + ), }, { - 'name': 'enable_peer_review_data_block', - 'object': setting_handler.get_setting('general', 'enable_peer_review_data_block', journal), + "name": "enable_peer_review_data_block", + "object": setting_handler.get_setting( + "general", "enable_peer_review_data_block", journal + ), }, { - 'name': 'hide_review_data_pre_release', - 'object': setting_handler.get_setting('general', 'hide_review_data_pre_release', journal), + "name": "hide_review_data_pre_release", + "object": setting_handler.get_setting( + "general", "hide_review_data_pre_release", journal + ), }, { - 'name': 'enable_suggested_reviewers', - 'object': setting_handler.get_setting('general', 'enable_suggested_reviewers', journal), + "name": "enable_suggested_reviewers", + "object": setting_handler.get_setting( + "general", "enable_suggested_reviewers", journal + ), }, { - 'name': 'enable_peer_review_data_on_review_page', - 'object': setting_handler.get_setting('general', 'enable_peer_review_data_on_review_page', journal), + "name": "enable_peer_review_data_on_review_page", + "object": setting_handler.get_setting( + "general", "enable_peer_review_data_on_review_page", journal + ), }, { - 'name': 'accept_article_warning', - 'object': setting_handler.get_setting('general', 'accept_article_warning', journal), + "name": "accept_article_warning", + "object": setting_handler.get_setting( + "general", "accept_article_warning", journal + ), }, { - 'name': 'open_peer_review', - 'object': setting_handler.get_setting('general', 'open_peer_review', journal), + "name": "open_peer_review", + "object": setting_handler.get_setting( + "general", "open_peer_review", journal + ), }, { - 'name': 'open_review_default_opt_in', - 'object': setting_handler.get_setting('general', 'open_review_default_opt_in', journal), + "name": "open_review_default_opt_in", + "object": setting_handler.get_setting( + "general", "open_review_default_opt_in", journal + ), }, { - 'name': 'disable_reviewer_recommendation', - 'object': setting_handler.get_setting('general', 'disable_reviewer_recommendation', journal), + "name": "disable_reviewer_recommendation", + "object": setting_handler.get_setting( + "general", "disable_reviewer_recommendation", journal + ), }, { - 'name': 'enable_share_reviews_decision', - 'object': setting_handler.get_setting('general', 'enable_share_reviews_decision', journal), + "name": "enable_share_reviews_decision", + "object": setting_handler.get_setting( + "general", "enable_share_reviews_decision", journal + ), }, { - 'name': 'display_completed_reviews_in_additional_rounds', - 'object': setting_handler.get_setting('general', 'display_completed_reviews_in_additional_rounds', journal), + "name": "display_completed_reviews_in_additional_rounds", + "object": setting_handler.get_setting( + "general", "display_completed_reviews_in_additional_rounds", journal + ), }, { - 'name': 'share_author_response_letters', - 'object': setting_handler.get_setting('general', 'share_author_response_letters', journal), + "name": "share_author_response_letters", + "object": setting_handler.get_setting( + "general", "share_author_response_letters", journal + ), }, { - 'name': 'display_completed_reviews_in_additional_rounds_text', - 'object': setting_handler.get_setting('general', 'display_completed_reviews_in_additional_rounds_text', journal), + "name": "display_completed_reviews_in_additional_rounds_text", + "object": setting_handler.get_setting( + "general", + "display_completed_reviews_in_additional_rounds_text", + journal, + ), }, ] - setting_group = 'general' + setting_group = "general" - elif display_group == 'crossref': + elif display_group == "crossref": xref_settings = [ - 'use_crossref', 'crossref_test', 'crossref_username', 'crossref_password', 'crossref_email', - 'crossref_name', 'crossref_prefix', 'crossref_registrant', 'doi_display_prefix', 'doi_display_suffix', - 'doi_pattern', 'doi_manager_action_maximum_size', 'title_doi', 'issue_doi_pattern', 'register_issue_dois' + "use_crossref", + "crossref_test", + "crossref_username", + "crossref_password", + "crossref_email", + "crossref_name", + "crossref_prefix", + "crossref_registrant", + "doi_display_prefix", + "doi_display_suffix", + "doi_pattern", + "doi_manager_action_maximum_size", + "title_doi", + "issue_doi_pattern", + "register_issue_dois", ] - group_of_settings = process_setting_list(xref_settings, 'Identifiers', journal) - setting_group = 'Identifiers' + group_of_settings = process_setting_list(xref_settings, "Identifiers", journal) + setting_group = "Identifiers" - elif display_group == 'crosscheck': - xref_settings = [ - 'enable', 'username', 'password' - ] + elif display_group == "crosscheck": + xref_settings = ["enable", "username", "password"] - group_of_settings = process_setting_list(xref_settings, 'crosscheck', journal) - setting_group = 'crosscheck' + group_of_settings = process_setting_list(xref_settings, "crosscheck", journal) + setting_group = "crosscheck" - elif display_group == 'journal': + elif display_group == "journal": journal_settings = [ - 'journal_name', 'journal_issn', 'print_issn', 'journal_theme', - 'journal_description', 'main_contact', 'publisher_name', - 'publisher_url', 'contact_info', 'privacy_policy_url', 'auto_signature', - 'slack_logging', 'slack_webhook', 'twitter_handle', - 'switch_language', 'enable_language_text', 'google_analytics_code', - 'use_ga_four', 'display_login_page_notice', 'login_page_notice', - 'display_register_page_notice', 'register_page_notice', - 'support_email', 'support_contact_message_for_staff', - 'from_address', 'replyto_address', + "journal_name", + "journal_issn", + "print_issn", + "journal_theme", + "journal_description", + "main_contact", + "publisher_name", + "publisher_url", + "contact_info", + "privacy_policy_url", + "auto_signature", + "slack_logging", + "slack_webhook", + "twitter_handle", + "switch_language", + "enable_language_text", + "google_analytics_code", + "use_ga_four", + "display_login_page_notice", + "login_page_notice", + "display_register_page_notice", + "register_page_notice", + "support_email", + "support_contact_message_for_staff", + "from_address", + "replyto_address", ] - group_of_settings = process_setting_list(journal_settings, 'general', journal) - group_of_settings[3]['choices'] = get_theme_list() - setting_group = 'general' + group_of_settings = process_setting_list(journal_settings, "general", journal) + group_of_settings[3]["choices"] = get_theme_list() + setting_group = "general" - if group_of_settings[3].get('object').value not in settings.CORE_THEMES: + if group_of_settings[3].get("object").value not in settings.CORE_THEMES: group_of_settings.append( { - 'name': 'journal_base_theme', - 'object': setting_handler.get_setting('general', 'journal_base_theme', journal), - 'choices': [ - [theme, theme] - for theme in settings.CORE_THEMES] + "name": "journal_base_theme", + "object": setting_handler.get_setting( + "general", "journal_base_theme", journal + ), + "choices": [[theme, theme] for theme in settings.CORE_THEMES], }, ) - elif display_group == 'proofing': - proofing_settings = [ - 'max_proofreaders' - ] - group_of_settings = process_setting_list(proofing_settings, 'general', journal) - setting_group = 'general' - elif display_group == 'article': + elif display_group == "proofing": + proofing_settings = ["max_proofreaders"] + group_of_settings = process_setting_list(proofing_settings, "general", journal) + setting_group = "general" + elif display_group == "article": article_settings = [ - 'suppress_how_to_cite', - 'disable_article_thumbnails', - 'disable_article_large_image', - 'display_guest_editors', - 'suppress_citations_metric', - 'display_altmetric_badge', - 'altmetric_badge_type', - 'hide_author_email_links', - 'display_date_submitted', - 'display_date_accepted', + "suppress_how_to_cite", + "disable_article_thumbnails", + "disable_article_large_image", + "display_guest_editors", + "suppress_citations_metric", + "display_altmetric_badge", + "altmetric_badge_type", + "hide_author_email_links", + "display_date_submitted", + "display_date_accepted", ] - group_of_settings = process_setting_list(article_settings, 'article', journal) - setting_group = 'article' - elif display_group == 'styling': + group_of_settings = process_setting_list(article_settings, "article", journal) + setting_group = "article" + elif display_group == "styling": group_of_settings = [ { - 'name': 'display_journal_title', - 'object': setting_handler.get_setting('styling', - 'display_journal_title', - journal), + "name": "display_journal_title", + "object": setting_handler.get_setting( + "styling", "display_journal_title", journal + ), }, ] - setting_group = 'styling' - elif display_group == 'editorial': + setting_group = "styling" + elif display_group == "editorial": group_of_settings = [ { - 'name': 'editorial_group_page_name', - 'object': setting_handler.get_setting('styling', - 'editorial_group_page_name', - journal), + "name": "editorial_group_page_name", + "object": setting_handler.get_setting( + "styling", "editorial_group_page_name", journal + ), }, { - 'name': 'hide_editorial_group_names', - 'object': setting_handler.get_setting('styling', - 'hide_editorial_group_names', - journal), + "name": "hide_editorial_group_names", + "object": setting_handler.get_setting( + "styling", "hide_editorial_group_names", journal + ), }, { - 'name': 'multi_page_editorial', - 'object': setting_handler.get_setting('styling', - 'multi_page_editorial', - journal), + "name": "multi_page_editorial", + "object": setting_handler.get_setting( + "styling", "multi_page_editorial", journal + ), }, { - 'name': 'display_countries_editorial_team', - 'object': setting_handler.get_setting('styling', - 'display_countries_editorial_team', - journal), - } + "name": "display_countries_editorial_team", + "object": setting_handler.get_setting( + "styling", "display_countries_editorial_team", journal + ), + }, ] - setting_group = 'styling' + setting_group = "styling" - elif display_group == 'news': + elif display_group == "news": group_of_settings = [ { - 'name': 'news_title', - 'object': setting_handler.get_setting('news', 'news_title', journal), + "name": "news_title", + "object": setting_handler.get_setting("news", "news_title", journal), }, ] - setting_group = 'news' + setting_group = "news" else: group_of_settings = [] setting_group = None @@ -549,7 +681,7 @@ def get_settings_to_edit(display_group, journal, user): # edit that setting, otherwise remove it from the group. for group_setting_item in group_of_settings[:]: if not user_can_edit_setting( - setting=group_setting_item['object'], + setting=group_setting_item["object"], user=user, journal=journal, ): @@ -578,16 +710,16 @@ def get_theme_list(): path = os.path.join(settings.BASE_DIR, "themes") root, dirs, files = next(os.walk(path)) - return [[dir, dir] for dir in dirs if dir not in ['admin', 'press', '__pycache__']] + return [[dir, dir] for dir in dirs if dir not in ["admin", "press", "__pycache__"]] def handle_default_thumbnail(request, journal, attr_form): - if request.FILES.get('default_thumbnail'): + if request.FILES.get("default_thumbnail"): new_file = files.save_file_to_journal( request, - request.FILES.get('default_thumbnail'), - 'Default Thumb', - 'default', + request.FILES.get("default_thumbnail"), + "Default Thumb", + "default", ) if journal.thumbnail_image: @@ -603,9 +735,9 @@ def handle_default_thumbnail(request, journal, attr_form): def article_file(uploaded_file, article, request): new_file = files.save_file_to_article(uploaded_file, article, request.user) - new_file.label = 'Banner image' - new_file.description = 'Banner image' - new_file.privacy = 'public' + new_file.label = "Banner image" + new_file.description = "Banner image" + new_file.privacy = "public" new_file.save() return new_file @@ -618,14 +750,12 @@ def handle_article_large_image_file(uploaded_file, article, request): article.save() else: new_file = files.overwrite_file( - uploaded_file, - article.large_image_file, - ('articles', article.pk) + uploaded_file, article.large_image_file, ("articles", article.pk) ) article.large_image_file = new_file article.save() - resize_and_crop(new_file.self_article_path(), [750, 324], 'middle') + resize_and_crop(new_file.self_article_path(), [750, 324], "middle") def handle_article_thumb_image_file(uploaded_file, article, request): @@ -636,9 +766,7 @@ def handle_article_thumb_image_file(uploaded_file, article, request): article.save() else: new_file = files.overwrite_file( - uploaded_file, - article.thumbnail_image_file, - ('articles', article.pk) + uploaded_file, article.thumbnail_image_file, ("articles", article.pk) ) article.thumbnail_image_file = new_file article.save() @@ -653,23 +781,23 @@ def handle_email_change(request, email_address): core_confirm_account_url = request.site_type.site_url( reverse( - 'core_confirm_account', - kwargs={'token': request.user.confirmation_code}, + "core_confirm_account", + kwargs={"token": request.user.confirmation_code}, ) ) context = { - 'user': request.user, - 'core_confirm_account_url': core_confirm_account_url, + "user": request.user, + "core_confirm_account_url": core_confirm_account_url, } message = render_template.get_message_content( request, context, - 'user_email_change', + "user_email_change", ) notify_helpers.send_email_with_body_from_user( request, - 'subject_user_email_change', + "subject_user_email_change", request.user.email, message, ) @@ -682,20 +810,24 @@ def handle_add_users_to_role(users, role, request): users = models.Account.objects.filter(pk__in=users) if not users: - messages.add_message(request, messages.WARNING, 'No users selected') + messages.add_message(request, messages.WARNING, "No users selected") if not role: - messages.add_message(request, messages.WARNING, 'No role selected.') + messages.add_message(request, messages.WARNING, "No role selected.") for user in users: user.add_account_role(role.slug, request.journal) - messages.add_message(request, messages.INFO, '{0} added to {1} role.'.format(user.full_name(), role.name)) + messages.add_message( + request, + messages.INFO, + "{0} added to {1} role.".format(user.full_name(), role.name), + ) def clear_active_elements(elements, workflow, plugins): elements_to_remove = list() for element in elements: - if workflow.elements.filter(handshake_url=element.get('handshake_url')): + if workflow.elements.filter(handshake_url=element.get("handshake_url")): elements_to_remove.append(element) for element in elements_to_remove: @@ -717,16 +849,20 @@ def get_available_elements(workflow): module_name = "{0}.plugin_settings".format(plugin) plugin_settings = import_module(module_name) - if hasattr(plugin_settings, 'IS_WORKFLOW_PLUGIN') and hasattr( - plugin_settings, 'HANDSHAKE_URL'): + if hasattr(plugin_settings, "IS_WORKFLOW_PLUGIN") and hasattr( + plugin_settings, "HANDSHAKE_URL" + ): if plugin_settings.IS_WORKFLOW_PLUGIN: our_elements.append( - {'name': plugin_settings.PLUGIN_NAME, - 'handshake_url': plugin_settings.HANDSHAKE_URL, - 'stage': plugin_settings.STAGE, - 'article_url': plugin_settings.ARTICLE_PK_IN_HANDSHAKE_URL, - 'jump_url': plugin_settings.JUMP_URL if hasattr(plugin_settings, 'JUMP_URL') else '', - } + { + "name": plugin_settings.PLUGIN_NAME, + "handshake_url": plugin_settings.HANDSHAKE_URL, + "stage": plugin_settings.STAGE, + "article_url": plugin_settings.ARTICLE_PK_IN_HANDSHAKE_URL, + "jump_url": plugin_settings.JUMP_URL + if hasattr(plugin_settings, "JUMP_URL") + else "", + } ) except ImportError as e: logger.error(e) @@ -736,12 +872,11 @@ def get_available_elements(workflow): def handle_element_post(workflow, element_name, request): for element in get_available_elements(workflow): - if element['name'] == element_name: + if element["name"] == element_name: defaults = { - 'jump_url': element.get('jump_url', ''), - 'stage': element['stage'], - 'handshake_url': element['handshake_url'], - + "jump_url": element.get("jump_url", ""), + "stage": element["stage"], + "handshake_url": element["handshake_url"], } element_obj, created = models.WorkflowElement.objects.get_or_create( journal=request.journal, @@ -753,7 +888,7 @@ def handle_element_post(workflow, element_name, request): def latest_articles(carousel, object_type): - if object_type == 'journal': + if object_type == "journal": carousel_objects = submission_models.Article.objects.filter( journal=carousel.journal, date_published__lte=timezone.now(), @@ -762,7 +897,8 @@ def latest_articles(carousel, object_type): else: carousel_objects = submission_models.Article.objects.filter( date_published__lte=timezone.now(), - stage=submission_models.STAGE_PUBLISHED, ).order_by("-date_published") + stage=submission_models.STAGE_PUBLISHED, + ).order_by("-date_published") return carousel_objects @@ -774,7 +910,7 @@ def selected_articles(carousel): def news_items(carousel, object_type, press=None): - if object_type == 'journal': + if object_type == "journal": object_id = carousel.journal.pk else: object_id = carousel.press.pk @@ -783,10 +919,10 @@ def news_items(carousel, object_type, press=None): return press.carousel_news_items.all() carousel_objects = comms_models.NewsItem.objects.filter( - (Q(content_type__model=object_type) & Q(object_id=object_id)) & - (Q(start_display__lte=timezone.now()) | Q(start_display=None)) & - (Q(end_display__gte=timezone.now()) | Q(end_display=None)) - ).order_by('-posted') + (Q(content_type__model=object_type) & Q(object_id=object_id)) + & (Q(start_display__lte=timezone.now()) | Q(start_display=None)) + & (Q(end_display__gte=timezone.now()) | Q(end_display=None)) + ).order_by("-posted") return carousel_objects @@ -796,8 +932,11 @@ def sort_mixed(article_objects, news_objects): for news_item in news_objects: for article in article_objects: - if article.date_published > news_item.posted and article not in carousel_objects: - carousel_objects.append(article) + if ( + article.date_published > news_item.posted + and article not in carousel_objects + ): + carousel_objects.append(article) carousel_objects.append(news_item) # add any articles that were not inserted during the above sort procedure @@ -810,11 +949,13 @@ def sort_mixed(article_objects, news_objects): def get_unpinned_articles(request, pinned_articles): articles_pinned = [pin.article.pk for pin in pinned_articles] - return submission_models.Article.objects.filter(journal=request.journal).exclude(pk__in=articles_pinned) + return submission_models.Article.objects.filter(journal=request.journal).exclude( + pk__in=articles_pinned + ) def order_pinned_articles(request, pinned_articles): - ids = [int(_id) for _id in request.POST.getlist('orders[]')] + ids = [int(_id) for _id in request.POST.getlist("orders[]")] for pin in pinned_articles: pin.sequence = ids.index(pin.pk) @@ -827,17 +968,26 @@ def password_policy_check(request): :param request: HTTPRequest object :return: An empty list or a list of errors. """ - password = request.POST.get('password_1') + password = request.POST.get("password_1") rules = [ - lambda s: len(password) >= request.press.password_length or _('Your password must be {} characters long').format(request.press.password_length) + lambda s: len(password) >= request.press.password_length + or _("Your password must be {} characters long").format( + request.press.password_length + ) ] if request.press.password_upper: - rules.append(lambda password: any(x.isupper() for x in password) or _('An uppercase character is required')) + rules.append( + lambda password: any(x.isupper() for x in password) + or _("An uppercase character is required") + ) if request.press.password_number: - rules.append(lambda password: any(x.isdigit() for x in password) or _('A number is required')) + rules.append( + lambda password: any(x.isdigit() for x in password) + or _("A number is required") + ) problems = [p for p in [r(password) for r in rules] if p != True] @@ -845,7 +995,7 @@ def password_policy_check(request): def get_ua_and_ip(request): - user_agent = request.META.get('HTTP_USER_AGENT', None) + user_agent = request.META.get("HTTP_USER_AGENT", None) ip_address = shared.get_ip_address(request) return user_agent, ip_address @@ -860,14 +1010,18 @@ def add_failed_login_attempt(request): def clear_bad_login_attempts(request): user_agent, ip_address = get_ua_and_ip(request) - models.LoginAttempt.objects.filter(user_agent=user_agent, ip_address=ip_address).delete() + models.LoginAttempt.objects.filter( + user_agent=user_agent, ip_address=ip_address + ).delete() def check_for_bad_login_attempts(request): user_agent, ip_address = get_ua_and_ip(request) time = timezone.now() - timedelta(minutes=10) - attempts = models.LoginAttempt.objects.filter(user_agent=user_agent, ip_address=ip_address, timestamp__gte=time) + attempts = models.LoginAttempt.objects.filter( + user_agent=user_agent, ip_address=ip_address, timestamp__gte=time + ) print(time, attempts.count()) return attempts.count() @@ -877,13 +1031,15 @@ def handle_file(request, setting_value, file): file_to_delete = models.File.objects.get(pk=setting_value.value) files.unlink_journal_file(request, file_to_delete) - file = files.save_file_to_journal(request, file, setting_value.setting.name, 'A setting file.') + file = files.save_file_to_journal( + request, file, setting_value.setting.name, "A setting file." + ) return file.pk def no_password_check(username): try: - check = models.Account.objects.get(username=username, password='') + check = models.Account.objects.get(username=username, password="") return check except models.Account.DoesNotExist: return False @@ -901,40 +1057,43 @@ def start_reset_process(request, account): def build_submission_list(request): section_list = list() my_assignments = False - order = 'pk' + order = "pk" to_exclude = [ submission_models.STAGE_PUBLISHED, submission_models.STAGE_REJECTED, - submission_models.STAGE_UNSUBMITTED + submission_models.STAGE_UNSUBMITTED, ] for key, value in request.POST.items(): - - if key.startswith('section_'): - section_id = re.findall(r'\d+', key) + if key.startswith("section_"): + section_id = re.findall(r"\d+", key) if section_id: section_list.append(Q(section__pk=section_id[0])) - elif key.startswith('my_assignments'): + elif key.startswith("my_assignments"): my_assignments = True - elif key.startswith('order'): - order = request.POST.get('order', 'pk') + elif key.startswith("order"): + order = request.POST.get("order", "pk") if not section_list: - section_list = [Q(section__pk=section.pk) for section in - submission_models.Section.objects.filter(journal=request.journal, is_filterable=True)] - - articles = submission_models.Article.objects.filter( - journal=request.journal).exclude( - stage__in=to_exclude - ).filter( - reduce(operator.or_, section_list) + section_list = [ + Q(section__pk=section.pk) + for section in submission_models.Section.objects.filter( + journal=request.journal, is_filterable=True + ) + ] + + articles = ( + submission_models.Article.objects.filter(journal=request.journal) + .exclude(stage__in=to_exclude) + .filter(reduce(operator.or_, section_list)) ) if my_assignments: - assignments = review_models.EditorAssignment.objects.filter(article__journal=request.journal, - editor=request.user) + assignments = review_models.EditorAssignment.objects.filter( + article__journal=request.journal, editor=request.user + ) assignment_article_pks = [assignment.article.pk for assignment in assignments] articles = articles.filter(pk__in=assignment_article_pks) @@ -951,9 +1110,19 @@ def create_html_snippet(name, object, template): def export_gdpr_user_profile(user): user = models.Account.objects.get(pk=user.pk) user_dict = model_to_dict(user) - [user_dict.pop(key) for key in - ['profile_image', 'interest', 'password', 'groups', 'user_permissions', 'activation_code', 'is_superuser', - 'is_staff']] + [ + user_dict.pop(key) + for key in [ + "profile_image", + "interest", + "password", + "groups", + "user_permissions", + "activation_code", + "is_superuser", + "is_staff", + ] + ] response = JsonResponse(user_dict) return response @@ -962,20 +1131,20 @@ def get_homepage_elements(request): homepage_elements = models.HomepageElement.objects.filter( content_type=request.model_content_type, object_id=request.site_type.pk, - active=True).order_by('sequence') + active=True, + ).order_by("sequence") homepage_element_names = [el.name for el in homepage_elements] return homepage_elements, homepage_element_names def render_nested_setting( - setting_name, - setting_group, - request, - article=None, - nested_settings=None, - ): - + setting_name, + setting_group, + request, + article=None, + nested_settings=None, +): setting = setting_handler.get_setting( setting_group, setting_name, @@ -985,22 +1154,17 @@ def render_nested_setting( setting_context = {} if article: - setting_context['article'] = article + setting_context["article"] = article if not nested_settings: - nested_settings=[] + nested_settings = [] for name, group in nested_settings: setting_context[name] = setting_handler.get_setting( - group, - name, - request.journal + group, name, request.journal ).processed_value rendered_string = render_template.get_message_content( - request, - setting_context, - setting, - template_is_setting=True + request, setting_context, setting, template_is_setting=True ) return rendered_string @@ -1008,8 +1172,7 @@ def render_nested_setting( def filter_articles_to_editor_assigned(request, articles): assignments = review_models.EditorAssignment.objects.filter( - article__journal=request.journal, - editor=request.user + article__journal=request.journal, editor=request.user ) assignment_article_pks = [assignment.article.pk for assignment in assignments] return articles.filter(pk__in=assignment_article_pks) diff --git a/src/core/middleware.py b/src/core/middleware.py index 9e868a7d8a..bcc68567b0 100755 --- a/src/core/middleware.py +++ b/src/core/middleware.py @@ -29,7 +29,7 @@ def get_site_resources(request): - """ Attempts to match the relevant resources for the request url + """Attempts to match the relevant resources for the request url Each site type is given a chance to resolve the url by domain or path :param request: A Django HttpRequest @@ -54,19 +54,17 @@ def get_site_resources(request): alias, site_path = core_models.DomainAlias.get_by_request(request) if alias and alias.redirect: logger.debug("Matched a redirect: %s" % alias.redirect_url) - redirect_obj = redirect( - alias.build_redirect_url(path=request.path)) + redirect_obj = redirect(alias.build_redirect_url(path=request.path)) elif alias: journal = alias.journal press = journal.press if journal else alias.press - # Couldn't match any resources if not press and not redirect_obj: logger.warning( "Couldn't match a resource for %s, redirecting to default: %s" "" % (request.path, settings.DEFAULT_HOST) - ) + ) redirect_obj = redirect(settings.DEFAULT_HOST) return journal, repository, press, redirect_obj, site_path @@ -75,7 +73,7 @@ def get_site_resources(request): class SiteSettingsMiddleware(BaseMiddleware): @staticmethod def process_request(request): - """ This middleware class sets a series of variables for templates + """This middleware class sets a series of variables for templates and views to access inside the request object. It defines what site is being requested based on the domain and/or path @@ -84,22 +82,26 @@ def process_request(request): """ journal, repository, press, redirect_obj, site_path = get_site_resources( - request) + request + ) if redirect_obj is not None: return redirect_obj - request.port = request.META['SERVER_PORT'] + request.port = request.META["SERVER_PORT"] request.press = press request.press_cover = press.press_cover(request) if journal is not None: logger.set_prefix(journal.code) request.journal = journal - request.journal_cover = journal.press_image_override.path if journal.press_image_override else None + request.journal_cover = ( + journal.press_image_override.path + if journal.press_image_override + else None + ) request.site_type = request.site_object = journal - request.model_content_type = ContentType.objects.get_for_model( - journal) + request.model_content_type = ContentType.objects.get_for_model(journal) request.repository = None elif repository is not None: @@ -120,11 +122,10 @@ def process_request(request): request.press_base_url = press.site_url() # Site search data urls - if ( - settings.SITE_SEARCH_INDEXING_FREQUENCY - and not settings.IN_TEST_RUNNER - ): - request.site_search_docs_url = cms_logic.get_search_data_url(request.press) + if settings.SITE_SEARCH_INDEXING_FREQUENCY and not settings.IN_TEST_RUNNER: + request.site_search_docs_url = cms_logic.get_search_data_url( + request.press + ) else: raise Http404() @@ -134,45 +135,51 @@ def process_request(request): prefix = "/" + site_path logger.debug("Setting script prefix to %s" % prefix) set_script_prefix(prefix) - request.path_info = request.path_info[len(prefix):] + request.path_info = request.path_info[len(prefix) :] # We check if the journal and press are set to be secure and redirect if the current request is not secure. if not request.is_secure(): if ( - request.site_object - and request.site_object.is_secure - and not settings.DEBUG + request.site_object + and request.site_object.is_secure + and not settings.DEBUG ): - return redirect("https://{0}{1}".format(request.get_host(), request.path)) + return redirect( + "https://{0}{1}".format(request.get_host(), request.path) + ) class MaintenanceModeMiddleware(BaseMiddleware): @staticmethod def process_request(request): if request.journal is not None: - maintenance_mode_setting = setting_handler.get_setting('general', 'maintenance_mode', request.journal) + maintenance_mode_setting = setting_handler.get_setting( + "general", "maintenance_mode", request.journal + ) if maintenance_mode_setting and maintenance_mode_setting.processed_value: - - if hasattr(request, 'user') and request.user.is_staff: + if hasattr(request, "user") and request.user.is_staff: return None - if request.path == '/login/': + if request.path == "/login/": return None - maintenance_mode_message = setting_handler.get_setting('general', 'maintenance_message', - request.journal) - request.META['maintenance_mode'] = maintenance_mode_message + maintenance_mode_message = setting_handler.get_setting( + "general", "maintenance_message", request.journal + ) + request.META["maintenance_mode"] = maintenance_mode_message raise PermissionDenied(request, maintenance_mode_message) class CounterCookieMiddleware(BaseMiddleware): - @staticmethod def process_response(request, response): try: - if not request.session.get('counter_tracking', None): - request.session['counter_tracking'] = str(uuid4()) - elif request.session.get('counter_tracking', None) and request.resolver_match.url_name == 'article_view': + if not request.session.get("counter_tracking", None): + request.session["counter_tracking"] = str(uuid4()) + elif ( + request.session.get("counter_tracking", None) + and request.resolver_match.url_name == "article_view" + ): request.session.modified = True except AttributeError: pass @@ -181,7 +188,6 @@ def process_response(request, response): class PressMiddleware(BaseMiddleware): - @staticmethod def process_request(request): if request.journal: @@ -189,15 +195,39 @@ def process_request(request): pass elif request.press: allowed_urls = [ - 'core_manager', 'core_manager_news', 'core_manager_edit_news', 'core_news_list', 'core_news_item', - 'news_file_download', 'core_flush_cache', 'core_login', 'core_login_orcid', 'core_register', - 'core_confirm_account', 'core_orcid_registration', 'core_get_reset_token', 'core_reset_password', - 'core_edit_profile', 'core_logout', 'press_cover_download', 'core_manager_index', - 'django_summernote-editor', 'django_summernote-upload_attachment', 'cms_index', 'cms_page_new', - 'cms_page_edit', 'cms_page', 'cms_nav', 'website_index', 'core_journal_contacts', - 'core_journal_contact', 'core_journal_contacts_order', 'contact', 'core_edit_settings_group', + "core_manager", + "core_manager_news", + "core_manager_edit_news", + "core_news_list", + "core_news_item", + "news_file_download", + "core_flush_cache", + "core_login", + "core_login_orcid", + "core_register", + "core_confirm_account", + "core_orcid_registration", + "core_get_reset_token", + "core_reset_password", + "core_edit_profile", + "core_logout", + "press_cover_download", + "core_manager_index", + "django_summernote-editor", + "django_summernote-upload_attachment", + "cms_index", + "cms_page_new", + "cms_page_edit", + "cms_page", + "cms_nav", + "website_index", + "core_journal_contacts", + "core_journal_contact", + "core_journal_contacts_order", + "contact", + "core_edit_settings_group", ] - allowed_pattern = 'press_' + allowed_pattern = "press_" if request.resolver_match: url_name = request.resolver_match.url_name @@ -208,7 +238,7 @@ def process_request(request): if url_name in allowed_urls or url_name.startswith(allowed_pattern): pass else: - raise Http404('Press cannot access this page.') + raise Http404("Press cannot access this page.") _threadlocal = threading.local() diff --git a/src/core/migrations/0001_initial.py b/src/core/migrations/0001_initial.py index 1eded68ec7..acca2665fd 100755 --- a/src/core/migrations/0001_initial.py +++ b/src/core/migrations/0001_initial.py @@ -10,345 +10,819 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Account', + name="Account", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')), - ('username', models.CharField(max_length=48, unique=True, verbose_name='Username')), - ('first_name', models.CharField(blank=True, max_length=300, null=True, verbose_name='First name')), - ('middle_name', models.CharField(blank=True, max_length=300, null=True, verbose_name='Middle name')), - ('last_name', models.CharField(blank=True, max_length=300, null=True, verbose_name='Last name')), - ('activation_code', models.CharField(blank=True, max_length=100, null=True)), - ('salutation', models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, null=True, verbose_name='Salutation')), - ('biography', models.TextField(blank=True, null=True, verbose_name='Biography')), - ('orcid', models.CharField(blank=True, max_length=40, null=True, verbose_name='ORCiD')), - ('institution', models.CharField(max_length=1000, verbose_name='Institution')), - ('department', models.CharField(blank=True, max_length=300, null=True, verbose_name='Department')), - ('twitter', models.CharField(blank=True, max_length=300, null=True, verbose_name='Twitter Handle')), - ('facebook', models.CharField(blank=True, max_length=300, null=True, verbose_name='Facebook Handle')), - ('linkedin', models.CharField(blank=True, max_length=300, null=True, verbose_name='Linkedin Profile')), - ('website', models.URLField(blank=True, max_length=300, null=True, verbose_name='Website')), - ('github', models.CharField(blank=True, max_length=300, null=True, verbose_name='Github Username')), - ('profile_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=core.models.profile_images_upload_path)), - ('email_sent', models.DateTimeField(blank=True, null=True)), - ('date_confirmed', models.DateTimeField(blank=True, null=True)), - ('confirmation_code', models.CharField(blank=True, max_length=200, null=True)), - ('signature', models.TextField(blank=True, null=True)), - ('is_active', models.BooleanField(default=False)), - ('is_staff', models.BooleanField(default=False)), - ('is_admin', models.BooleanField(default=False)), - ('enable_digest', models.BooleanField(default=False)), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "email", + models.EmailField( + max_length=254, unique=True, verbose_name="Email" + ), + ), + ( + "username", + models.CharField( + max_length=48, unique=True, verbose_name="Username" + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=300, null=True, verbose_name="First name" + ), + ), + ( + "middle_name", + models.CharField( + blank=True, + max_length=300, + null=True, + verbose_name="Middle name", + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=300, null=True, verbose_name="Last name" + ), + ), + ( + "activation_code", + models.CharField(blank=True, max_length=100, null=True), + ), + ( + "salutation", + models.CharField( + blank=True, + choices=[ + ("Miss", "Miss"), + ("Ms", "Ms"), + ("Mrs", "Mrs"), + ("Mr", "Mr"), + ("Mx", "Mx"), + ("Dr", "Dr"), + ("Prof.", "Prof."), + ], + max_length=10, + null=True, + verbose_name="Salutation", + ), + ), + ( + "biography", + models.TextField(blank=True, null=True, verbose_name="Biography"), + ), + ( + "orcid", + models.CharField( + blank=True, max_length=40, null=True, verbose_name="ORCiD" + ), + ), + ( + "institution", + models.CharField(max_length=1000, verbose_name="Institution"), + ), + ( + "department", + models.CharField( + blank=True, max_length=300, null=True, verbose_name="Department" + ), + ), + ( + "twitter", + models.CharField( + blank=True, + max_length=300, + null=True, + verbose_name="Twitter Handle", + ), + ), + ( + "facebook", + models.CharField( + blank=True, + max_length=300, + null=True, + verbose_name="Facebook Handle", + ), + ), + ( + "linkedin", + models.CharField( + blank=True, + max_length=300, + null=True, + verbose_name="Linkedin Profile", + ), + ), + ( + "website", + models.URLField( + blank=True, max_length=300, null=True, verbose_name="Website" + ), + ), + ( + "github", + models.CharField( + blank=True, + max_length=300, + null=True, + verbose_name="Github Username", + ), + ), + ( + "profile_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=core.models.profile_images_upload_path, + ), + ), + ("email_sent", models.DateTimeField(blank=True, null=True)), + ("date_confirmed", models.DateTimeField(blank=True, null=True)), + ( + "confirmation_code", + models.CharField(blank=True, max_length=200, null=True), + ), + ("signature", models.TextField(blank=True, null=True)), + ("is_active", models.BooleanField(default=False)), + ("is_staff", models.BooleanField(default=False)), + ("is_admin", models.BooleanField(default=False)), + ("enable_digest", models.BooleanField(default=False)), + ( + "date_joined", + models.DateTimeField(default=django.utils.timezone.now), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='AccountRole', + name="AccountRole", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], ), migrations.CreateModel( - name='Contact', + name="Contact", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('recipient', models.EmailField(max_length=200, verbose_name='Who would you like to contact?')), - ('sender', models.EmailField(max_length=200, verbose_name='Your contact email address')), - ('subject', models.CharField(max_length=300, verbose_name='Subject')), - ('body', models.TextField(verbose_name='Your message')), - ('client_ip', models.GenericIPAddressField()), - ('date_sent', models.DateField(auto_now_add=True)), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "recipient", + models.EmailField( + max_length=200, verbose_name="Who would you like to contact?" + ), + ), + ( + "sender", + models.EmailField( + max_length=200, verbose_name="Your contact email address" + ), + ), + ("subject", models.CharField(max_length=300, verbose_name="Subject")), + ("body", models.TextField(verbose_name="Your message")), + ("client_ip", models.GenericIPAddressField()), + ("date_sent", models.DateField(auto_now_add=True)), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), ], options={ - 'verbose_name_plural': 'contact messages', + "verbose_name_plural": "contact messages", }, ), migrations.CreateModel( - name='Contacts', + name="Contacts", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('name', models.CharField(max_length=300)), - ('email', models.EmailField(max_length=254)), - ('role', models.CharField(max_length=200)), - ('sequence', models.PositiveIntegerField(default=999)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("name", models.CharField(max_length=300)), + ("email", models.EmailField(max_length=254)), + ("role", models.CharField(max_length=200)), + ("sequence", models.PositiveIntegerField(default=999)), ], options={ - 'verbose_name_plural': 'contacts', - 'ordering': ('sequence', 'name'), + "verbose_name_plural": "contacts", + "ordering": ("sequence", "name"), }, ), migrations.CreateModel( - name='Country', + name="Country", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('code', models.TextField(max_length=5)), - ('name', models.TextField(max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("code", models.TextField(max_length=5)), + ("name", models.TextField(max_length=255)), ], options={ - 'verbose_name_plural': 'countries', - 'ordering': ('name', 'code'), + "verbose_name_plural": "countries", + "ordering": ("name", "code"), }, ), migrations.CreateModel( - name='DomainAlias', + name="DomainAlias", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('domain', models.CharField(max_length=255)), - ('redirect', models.BooleanField(default=True, help_text='If enabled, the site will throw a 301 redirect to the master domain.', verbose_name='301')), - ('site_id', models.PositiveIntegerField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("domain", models.CharField(max_length=255)), + ( + "redirect", + models.BooleanField( + default=True, + help_text="If enabled, the site will throw a 301 redirect to the master domain.", + verbose_name="301", + ), + ), + ("site_id", models.PositiveIntegerField()), ], options={ - 'verbose_name_plural': 'domain aliases', + "verbose_name_plural": "domain aliases", }, ), migrations.CreateModel( - name='EditorialGroup', + name="EditorialGroup", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=500)), - ('description', models.TextField(blank=True, null=True)), - ('sequence', models.PositiveIntegerField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=500)), + ("description", models.TextField(blank=True, null=True)), + ("sequence", models.PositiveIntegerField()), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), migrations.CreateModel( - name='EditorialGroupMember', + name="EditorialGroupMember", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sequence', models.PositiveIntegerField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("sequence", models.PositiveIntegerField()), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), migrations.CreateModel( - name='File', + name="File", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('article_id', models.PositiveIntegerField(blank=True, null=True, verbose_name='Article PK')), - ('mime_type', models.CharField(max_length=255)), - ('original_filename', models.CharField(max_length=1000)), - ('uuid_filename', models.CharField(max_length=100)), - ('label', models.CharField(blank=True, max_length=200, null=True, verbose_name='Label')), - ('description', models.TextField(blank=True, null=True, verbose_name='Description')), - ('sequence', models.IntegerField(default=1)), - ('privacy', models.CharField(choices=[('public', 'Public'), ('typesetters', 'Typesetters'), ('proofreaders', 'Proofreaders'), ('copyeditors', 'Copyedtiors'), ('editors', 'Editors'), ('owner', 'Owner')], default='owner', max_length=20)), - ('date_uploaded', models.DateTimeField(auto_now_add=True)), - ('date_modified', models.DateTimeField(auto_now=True)), - ('is_galley', models.BooleanField(default=False)), - ('is_remote', models.BooleanField(default=False)), - ('remote_url', models.URLField(blank=True, null=True, verbose_name='Remote URL of file')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "article_id", + models.PositiveIntegerField( + blank=True, null=True, verbose_name="Article PK" + ), + ), + ("mime_type", models.CharField(max_length=255)), + ("original_filename", models.CharField(max_length=1000)), + ("uuid_filename", models.CharField(max_length=100)), + ( + "label", + models.CharField( + blank=True, max_length=200, null=True, verbose_name="Label" + ), + ), + ( + "description", + models.TextField(blank=True, null=True, verbose_name="Description"), + ), + ("sequence", models.IntegerField(default=1)), + ( + "privacy", + models.CharField( + choices=[ + ("public", "Public"), + ("typesetters", "Typesetters"), + ("proofreaders", "Proofreaders"), + ("copyeditors", "Copyedtiors"), + ("editors", "Editors"), + ("owner", "Owner"), + ], + default="owner", + max_length=20, + ), + ), + ("date_uploaded", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), + ("is_galley", models.BooleanField(default=False)), + ("is_remote", models.BooleanField(default=False)), + ( + "remote_url", + models.URLField( + blank=True, null=True, verbose_name="Remote URL of file" + ), + ), ], options={ - 'ordering': ('sequence', 'pk'), + "ordering": ("sequence", "pk"), }, ), migrations.CreateModel( - name='FileHistory', + name="FileHistory", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('article_id', models.PositiveIntegerField(blank=True, null=True, verbose_name='Article PK')), - ('mime_type', models.CharField(max_length=255)), - ('original_filename', models.CharField(max_length=1000)), - ('uuid_filename', models.CharField(max_length=100)), - ('label', models.CharField(blank=True, max_length=200, null=True, verbose_name='Label')), - ('description', models.TextField(blank=True, null=True, verbose_name='Description')), - ('sequence', models.IntegerField(default=1)), - ('privacy', models.CharField(choices=[('public', 'Public'), ('typesetters', 'Typesetters'), ('proofreaders', 'Proofreaders'), ('copyeditors', 'Copyedtiors'), ('editors', 'Editors'), ('owner', 'Owner')], default='owner', max_length=20)), - ('history_seq', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "article_id", + models.PositiveIntegerField( + blank=True, null=True, verbose_name="Article PK" + ), + ), + ("mime_type", models.CharField(max_length=255)), + ("original_filename", models.CharField(max_length=1000)), + ("uuid_filename", models.CharField(max_length=100)), + ( + "label", + models.CharField( + blank=True, max_length=200, null=True, verbose_name="Label" + ), + ), + ( + "description", + models.TextField(blank=True, null=True, verbose_name="Description"), + ), + ("sequence", models.IntegerField(default=1)), + ( + "privacy", + models.CharField( + choices=[ + ("public", "Public"), + ("typesetters", "Typesetters"), + ("proofreaders", "Proofreaders"), + ("copyeditors", "Copyedtiors"), + ("editors", "Editors"), + ("owner", "Owner"), + ], + default="owner", + max_length=20, + ), + ), + ("history_seq", models.PositiveIntegerField(default=0)), ], options={ - 'ordering': ('history_seq',), - 'verbose_name_plural': 'file histories', + "ordering": ("history_seq",), + "verbose_name_plural": "file histories", }, ), migrations.CreateModel( - name='Galley', + name="Galley", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_remote', models.BooleanField(default=False)), - ('remote_file', models.URLField(blank=True, null=True)), - ('label', models.CharField(max_length=400)), - ('type', models.CharField(choices=[('pdf', 'PDF'), ('epub', 'EPUB'), ('html', 'HTML'), ('xml', 'XML'), ('doc', 'Word (Doc)'), ('docx', 'Word (DOCX)'), ('odt', 'OpenDocument Text Document'), ('tex', 'LaTeX'), ('rtf', 'RTF')], max_length=100)), - ('sequence', models.IntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_remote", models.BooleanField(default=False)), + ("remote_file", models.URLField(blank=True, null=True)), + ("label", models.CharField(max_length=400)), + ( + "type", + models.CharField( + choices=[ + ("pdf", "PDF"), + ("epub", "EPUB"), + ("html", "HTML"), + ("xml", "XML"), + ("doc", "Word (Doc)"), + ("docx", "Word (DOCX)"), + ("odt", "OpenDocument Text Document"), + ("tex", "LaTeX"), + ("rtf", "RTF"), + ], + max_length=100, + ), + ), + ("sequence", models.IntegerField(default=0)), ], ), migrations.CreateModel( - name='HomepageElement', + name="HomepageElement", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('configure_url', models.CharField(blank=True, max_length=200, null=True)), - ('name', models.CharField(max_length=200)), - ('template_path', models.CharField(max_length=500)), - ('sequence', models.PositiveIntegerField(default=999)), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('available_to_press', models.BooleanField(default=False, help_text='Determines if this element is available for the press.')), - ('active', models.BooleanField(default=False)), - ('has_config', models.BooleanField(default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "configure_url", + models.CharField(blank=True, max_length=200, null=True), + ), + ("name", models.CharField(max_length=200)), + ("template_path", models.CharField(max_length=500)), + ("sequence", models.PositiveIntegerField(default=999)), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "available_to_press", + models.BooleanField( + default=False, + help_text="Determines if this element is available for the press.", + ), + ), + ("active", models.BooleanField(default=False)), + ("has_config", models.BooleanField(default=True)), ], options={ - 'verbose_name_plural': 'Homepage Elements', - 'ordering': ('sequence', 'name'), + "verbose_name_plural": "Homepage Elements", + "ordering": ("sequence", "name"), }, ), migrations.CreateModel( - name='Interest', + name="Interest", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=250)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=250)), ], ), migrations.CreateModel( - name='NewsItem', + name="NewsItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('title', models.CharField(max_length=500)), - ('body', models.TextField()), - ('posted', models.DateTimeField(default=django.utils.timezone.now)), - ('start_display', models.DateField(default=django.utils.timezone.now)), - ('end_display', models.DateField(blank=True, null=True)), - ('sequence', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("title", models.CharField(max_length=500)), + ("body", models.TextField()), + ("posted", models.DateTimeField(default=django.utils.timezone.now)), + ("start_display", models.DateField(default=django.utils.timezone.now)), + ("end_display", models.DateField(blank=True, null=True)), + ("sequence", models.PositiveIntegerField(default=0)), ], options={ - 'ordering': ('-posted', 'title'), + "ordering": ("-posted", "title"), }, ), migrations.CreateModel( - name='OrcidToken', + name="OrcidToken", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('token', models.UUIDField(default=uuid.uuid4)), - ('orcid', models.CharField(max_length=200)), - ('expiry', models.DateTimeField(default=core.models.generate_expiry_date, verbose_name='Expires on')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("token", models.UUIDField(default=uuid.uuid4)), + ("orcid", models.CharField(max_length=200)), + ( + "expiry", + models.DateTimeField( + default=core.models.generate_expiry_date, + verbose_name="Expires on", + ), + ), ], ), migrations.CreateModel( - name='PasswordResetToken', + name="PasswordResetToken", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('token', models.CharField(default=uuid.uuid4, max_length=300)), - ('expiry', models.DateTimeField(default=core.models.generate_expiry_date, verbose_name='Expires on')), - ('expired', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("token", models.CharField(default=uuid.uuid4, max_length=300)), + ( + "expiry", + models.DateTimeField( + default=core.models.generate_expiry_date, + verbose_name="Expires on", + ), + ), + ("expired", models.BooleanField(default=False)), ], options={ - 'ordering': ['-expiry'], + "ordering": ["-expiry"], }, ), migrations.CreateModel( - name='Role', + name="Role", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100, help_text='Display name for this role (can include spaces and capital letters)')), - ('slug', models.CharField(max_length=100, help_text='Normalized string representing this role containing only lowercase letters and hyphens.')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + max_length=100, + help_text="Display name for this role (can include spaces and capital letters)", + ), + ), + ( + "slug", + models.CharField( + max_length=100, + help_text="Normalized string representing this role containing only lowercase letters and hyphens.", + ), + ), ], options={ - 'ordering': ('name', 'slug'), + "ordering": ("name", "slug"), }, ), migrations.CreateModel( - name='Setting', + name="Setting", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('types', models.CharField(choices=[('rich-text', 'Rich Text'), ('text', 'Text'), ('char', 'Characters'), ('number', 'Number'), ('boolean', 'Boolean'), ('file', 'File'), ('select', 'Select'), ('json', 'JSON')], max_length=20)), - ('pretty_name', models.CharField(default='', max_length=100)), - ('description', models.TextField(blank=True, null=True)), - ('is_translatable', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ( + "types", + models.CharField( + choices=[ + ("rich-text", "Rich Text"), + ("text", "Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), + ], + max_length=20, + ), + ), + ("pretty_name", models.CharField(default="", max_length=100)), + ("description", models.TextField(blank=True, null=True)), + ("is_translatable", models.BooleanField(default=False)), ], options={ - 'ordering': ('group', 'name'), + "ordering": ("group", "name"), }, ), migrations.CreateModel( - name='SettingGroup', + name="SettingGroup", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('enabled', models.BooleanField(default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ("enabled", models.BooleanField(default=True)), ], ), migrations.CreateModel( - name='SettingValue', + name="SettingValue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='SettingValueTranslation', + name="SettingValueTranslation", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('value', models.TextField(blank=True, null=True)), - ('language_code', models.CharField(db_index=True, max_length=15)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.TextField(blank=True, null=True)), + ("language_code", models.CharField(db_index=True, max_length=15)), ], options={ - 'db_table': 'core_settingvalue_translation', - 'db_tablespace': '', - 'managed': True, - 'abstract': False, - 'default_permissions': (), + "db_table": "core_settingvalue_translation", + "db_tablespace": "", + "managed": True, + "abstract": False, + "default_permissions": (), }, ), migrations.CreateModel( - name='Task', + name="Task", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('title', models.CharField(max_length=300)), - ('description', models.TextField()), - ('link', models.TextField(blank=True, help_text='A url name, where the action of this task can undertaken', null=True)), - ('created', models.DateTimeField(default=django.utils.timezone.now)), - ('due', models.DateTimeField(blank=True, null=True)), - ('completed', models.DateTimeField(blank=True, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("title", models.CharField(max_length=300)), + ("description", models.TextField()), + ( + "link", + models.TextField( + blank=True, + help_text="A url name, where the action of this task can undertaken", + null=True, + ), + ), + ("created", models.DateTimeField(default=django.utils.timezone.now)), + ("due", models.DateTimeField(blank=True, null=True)), + ("completed", models.DateTimeField(blank=True, null=True)), ], ), migrations.CreateModel( - name='TaskCompleteEvents', + name="TaskCompleteEvents", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('event_name', models.CharField(max_length=300)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("event_name", models.CharField(max_length=300)), ], options={ - 'verbose_name_plural': 'task complete events', + "verbose_name_plural": "task complete events", }, ), migrations.CreateModel( - name='Workflow', + name="Workflow", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], ), migrations.CreateModel( - name='WorkflowElement', + name="WorkflowElement", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('element_name', models.CharField(max_length=255)), - ('handshake_url', models.CharField(max_length=255)), - ('stage', models.CharField(default='Unassigned', max_length=255)), - ('order', models.PositiveIntegerField(default=20)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("element_name", models.CharField(max_length=255)), + ("handshake_url", models.CharField(max_length=255)), + ("stage", models.CharField(default="Unassigned", max_length=255)), + ("order", models.PositiveIntegerField(default=20)), ], options={ - 'ordering': ('order', 'element_name'), + "ordering": ("order", "element_name"), }, ), ] diff --git a/src/core/migrations/0002_auto_20170711_1203.py b/src/core/migrations/0002_auto_20170711_1203.py index c1213aaeb3..0ef5eed4cd 100755 --- a/src/core/migrations/0002_auto_20170711_1203.py +++ b/src/core/migrations/0002_auto_20170711_1203.py @@ -8,199 +8,309 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('core', '0001_initial'), - ('submission', '0001_initial'), - ('journal', '0002_auto_20170711_1203'), - ('auth', '0007_alter_validators_add_error_messages'), - ('contenttypes', '0002_remove_content_type_name'), + ("core", "0001_initial"), + ("submission", "0001_initial"), + ("journal", "0002_auto_20170711_1203"), + ("auth", "0007_alter_validators_add_error_messages"), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.AddField( - model_name='workflowelement', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="workflowelement", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), migrations.AddField( - model_name='workflow', - name='elements', - field=models.ManyToManyField(to='core.WorkflowElement'), + model_name="workflow", + name="elements", + field=models.ManyToManyField(to="core.WorkflowElement"), ), migrations.AddField( - model_name='workflow', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="workflow", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), migrations.AddField( - model_name='task', - name='assignees', + model_name="task", + name="assignees", field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), ), migrations.AddField( - model_name='task', - name='complete_events', - field=models.ManyToManyField(to='core.TaskCompleteEvents'), - ), - migrations.AddField( - model_name='task', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='completed_by', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='task', - name='content_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='task_content_type', to='contenttypes.ContentType'), - ), - migrations.AddField( - model_name='settingvaluetranslation', - name='master', - field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='core.SettingValue'), - ), - migrations.AddField( - model_name='settingvalue', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), - migrations.AddField( - model_name='settingvalue', - name='setting', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Setting'), - ), - migrations.AddField( - model_name='setting', - name='group', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.SettingGroup'), - ), - migrations.AddField( - model_name='passwordresettoken', - name='account', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='newsitem', - name='content_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='news_content_type', to='contenttypes.ContentType'), - ), - migrations.AddField( - model_name='newsitem', - name='large_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='news_file', to='core.File'), - ), - migrations.AddField( - model_name='newsitem', - name='posted_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='homepageelement', - name='content_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='element_content_type', to='contenttypes.ContentType'), - ), - migrations.AddField( - model_name='galley', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), - ), - migrations.AddField( - model_name='galley', - name='css_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='css_file', to='core.File'), - ), - migrations.AddField( - model_name='galley', - name='file', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.File'), - ), - migrations.AddField( - model_name='galley', - name='images', - field=models.ManyToManyField(blank=True, null=True, related_name='images', to='core.File'), - ), - migrations.AddField( - model_name='filehistory', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='file', - name='history', - field=models.ManyToManyField(to='core.FileHistory'), - ), - migrations.AddField( - model_name='file', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='editorialgroupmember', - name='group', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.EditorialGroup'), - ), - migrations.AddField( - model_name='editorialgroupmember', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='editorialgroup', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), - migrations.AddField( - model_name='contacts', - name='content_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_content_type', to='contenttypes.ContentType'), - ), - migrations.AddField( - model_name='contact', - name='content_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_c_t', to='contenttypes.ContentType'), - ), - migrations.AddField( - model_name='accountrole', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), + model_name="task", + name="complete_events", + field=models.ManyToManyField(to="core.TaskCompleteEvents"), + ), migrations.AddField( - model_name='accountrole', - name='role', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Role'), + model_name="task", + name="completed_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="completed_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="task", + name="content_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="task_content_type", + to="contenttypes.ContentType", + ), + ), + migrations.AddField( + model_name="settingvaluetranslation", + name="master", + field=models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="core.SettingValue", + ), + ), + migrations.AddField( + model_name="settingvalue", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="settingvalue", + name="setting", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.Setting" + ), + ), + migrations.AddField( + model_name="setting", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.SettingGroup" + ), + ), + migrations.AddField( + model_name="passwordresettoken", + name="account", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.AddField( + model_name="newsitem", + name="content_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="news_content_type", + to="contenttypes.ContentType", + ), ), migrations.AddField( - model_name='accountrole', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="newsitem", + name="large_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="news_file", + to="core.File", + ), ), migrations.AddField( - model_name='account', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Country', verbose_name='Country'), + model_name="newsitem", + name="posted_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="homepageelement", + name="content_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="element_content_type", + to="contenttypes.ContentType", + ), + ), + migrations.AddField( + model_name="galley", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), + ), + migrations.AddField( + model_name="galley", + name="css_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="css_file", + to="core.File", + ), ), migrations.AddField( - model_name='account', - name='groups', - field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + model_name="galley", + name="file", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.File" + ), + ), + migrations.AddField( + model_name="galley", + name="images", + field=models.ManyToManyField( + blank=True, null=True, related_name="images", to="core.File" + ), + ), + migrations.AddField( + model_name="filehistory", + name="owner", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="file", + name="history", + field=models.ManyToManyField(to="core.FileHistory"), + ), + migrations.AddField( + model_name="file", + name="owner", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="editorialgroupmember", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.EditorialGroup" + ), + ), + migrations.AddField( + model_name="editorialgroupmember", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.AddField( + model_name="editorialgroup", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="contacts", + name="content_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_content_type", + to="contenttypes.ContentType", + ), + ), + migrations.AddField( + model_name="contact", + name="content_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_c_t", + to="contenttypes.ContentType", + ), + ), + migrations.AddField( + model_name="accountrole", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="accountrole", + name="role", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.Role" + ), + ), + migrations.AddField( + model_name="accountrole", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), ), migrations.AddField( - model_name='account', - name='interest', - field=models.ManyToManyField(blank=True, null=True, to='core.Interest'), + model_name="account", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Country", + verbose_name="Country", + ), + ), + migrations.AddField( + model_name="account", + name="groups", + field=models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), ), migrations.AddField( - model_name='account', - name='user_permissions', - field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + model_name="account", + name="interest", + field=models.ManyToManyField(blank=True, null=True, to="core.Interest"), + ), + migrations.AddField( + model_name="account", + name="user_permissions", + field=models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), ), migrations.AlterUniqueTogether( - name='settingvaluetranslation', - unique_together=set([('language_code', 'master')]), + name="settingvaluetranslation", + unique_together=set([("language_code", "master")]), ), migrations.AlterUniqueTogether( - name='homepageelement', - unique_together=set([('name', 'content_type', 'object_id')]), + name="homepageelement", + unique_together=set([("name", "content_type", "object_id")]), ), ] diff --git a/src/core/migrations/0003_load_countries.py b/src/core/migrations/0003_load_countries.py index b929814033..d108a3cb0f 100755 --- a/src/core/migrations/0003_load_countries.py +++ b/src/core/migrations/0003_load_countries.py @@ -6,24 +6,27 @@ class Migration(migrations.Migration): - def forwards_func(apps, schema_editor): from core.models import COUNTRY_CHOICES + Country = apps.get_model("core", "Country") db_alias = schema_editor.connection.alias for choice in COUNTRY_CHOICES: - Country.objects.using(db_alias).get_or_create(code=choice[0], name=choice[1]) + Country.objects.using(db_alias).get_or_create( + code=choice[0], name=choice[1] + ) def reverse_func(apps, schema_editor): from core.models import COUNTRY_CHOICES + Country = apps.get_model("core", "Country") db_alias = schema_editor.connection.alias Country.objects.using(db_alias).all().delete() dependencies = [ - ('core', '0002_auto_20170711_1203'), + ("core", "0002_auto_20170711_1203"), ] operations = [ diff --git a/src/core/migrations/0004_auto_20170813_1302.py b/src/core/migrations/0004_auto_20170813_1302.py index 32d43fae6b..14d4dd425f 100755 --- a/src/core/migrations/0004_auto_20170813_1302.py +++ b/src/core/migrations/0004_auto_20170813_1302.py @@ -9,31 +9,37 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0004_auto_20170813_1302'), - ('core', '0003_load_countries'), + ("journal", "0004_auto_20170813_1302"), + ("core", "0003_load_countries"), ] operations = [ migrations.AlterModelOptions( - name='settingvalue', - options={'base_manager_name': '_plain_manager'}, + name="settingvalue", + options={"base_manager_name": "_plain_manager"}, ), migrations.AlterModelManagers( - name='settingvalue', + name="settingvalue", managers=[ - ('objects', django.db.models.manager.Manager()), - ('_plain_manager', django.db.models.manager.Manager()), + ("objects", django.db.models.manager.Manager()), + ("_plain_manager", django.db.models.manager.Manager()), ], ), migrations.AlterField( - model_name='account', - name='profile_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=core.models.profile_images_upload_path), + model_name="account", + name="profile_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=core.models.profile_images_upload_path, + ), ), migrations.AlterUniqueTogether( - name='accountrole', - unique_together=set([('journal', 'user', 'role')]), + name="accountrole", + unique_together=set([("journal", "user", "role")]), ), ] diff --git a/src/core/migrations/0005_auto_20170816_0851.py b/src/core/migrations/0005_auto_20170816_0851.py index da522a934a..a5a4891865 100755 --- a/src/core/migrations/0005_auto_20170816_0851.py +++ b/src/core/migrations/0005_auto_20170816_0851.py @@ -6,7 +6,6 @@ class Migration(migrations.Migration): - def forwards_func(apps, schema_editor): NewsItem = apps.get_model("core", "NewsItem") CommsItem = apps.get_model("comms", "NewsItem") @@ -33,7 +32,7 @@ def reverse_func(apps, schema_editor): pass dependencies = [ - ('core', '0004_auto_20170813_1302'), + ("core", "0004_auto_20170813_1302"), ] operations = [ diff --git a/src/core/migrations/0006_auto_20170816_0900.py b/src/core/migrations/0006_auto_20170816_0900.py index 0b4e631b19..c8fbc8f866 100755 --- a/src/core/migrations/0006_auto_20170816_0900.py +++ b/src/core/migrations/0006_auto_20170816_0900.py @@ -6,54 +6,53 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0005_auto_20170816_0851'), + ("core", "0005_auto_20170816_0851"), ] operations = [ migrations.AlterModelOptions( - name='newsitem', + name="newsitem", options={}, ), migrations.RemoveField( - model_name='newsitem', - name='body', + model_name="newsitem", + name="body", ), migrations.RemoveField( - model_name='newsitem', - name='content_type', + model_name="newsitem", + name="content_type", ), migrations.RemoveField( - model_name='newsitem', - name='end_display', + model_name="newsitem", + name="end_display", ), migrations.RemoveField( - model_name='newsitem', - name='large_image_file', + model_name="newsitem", + name="large_image_file", ), migrations.RemoveField( - model_name='newsitem', - name='object_id', + model_name="newsitem", + name="object_id", ), migrations.RemoveField( - model_name='newsitem', - name='posted', + model_name="newsitem", + name="posted", ), migrations.RemoveField( - model_name='newsitem', - name='posted_by', + model_name="newsitem", + name="posted_by", ), migrations.RemoveField( - model_name='newsitem', - name='sequence', + model_name="newsitem", + name="sequence", ), migrations.RemoveField( - model_name='newsitem', - name='start_display', + model_name="newsitem", + name="start_display", ), migrations.RemoveField( - model_name='newsitem', - name='title', + model_name="newsitem", + name="title", ), ] diff --git a/src/core/migrations/0007_auto_20170829_1501.py b/src/core/migrations/0007_auto_20170829_1501.py index 7fe59fcf7b..e1082f075f 100755 --- a/src/core/migrations/0007_auto_20170829_1501.py +++ b/src/core/migrations/0007_auto_20170829_1501.py @@ -7,23 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0003_auto_20170829_1501'), - ('core', '0006_auto_20170816_0900'), + ("press", "0003_auto_20170829_1501"), + ("core", "0006_auto_20170816_0900"), ] operations = [ migrations.CreateModel( - name='LoginAttempt', + name="LoginAttempt", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ip_address', models.GenericIPAddressField()), - ('user_agent', models.TextField()), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("ip_address", models.GenericIPAddressField()), + ("user_agent", models.TextField()), + ("timestamp", models.DateTimeField(default=django.utils.timezone.now)), ], ), migrations.DeleteModel( - name='NewsItem', + name="NewsItem", ), ] diff --git a/src/core/migrations/0008_auto_20170925_1129.py b/src/core/migrations/0008_auto_20170925_1129.py index 70afc131ee..7374313b1d 100755 --- a/src/core/migrations/0008_auto_20170925_1129.py +++ b/src/core/migrations/0008_auto_20170925_1129.py @@ -9,20 +9,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0007_auto_20170829_1501'), + ("core", "0007_auto_20170829_1501"), ] operations = [ migrations.AddField( - model_name='account', - name='uuid', + model_name="account", + name="uuid", field=models.UUIDField(default=uuid.uuid4, editable=False), ), migrations.AlterField( - model_name='account', - name='profile_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=core.models.profile_images_upload_path), + model_name="account", + name="profile_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=core.models.profile_images_upload_path, + ), ), ] diff --git a/src/core/migrations/0009_auto_20170925_1133.py b/src/core/migrations/0009_auto_20170925_1133.py index 712d60a48a..b3d46a814b 100755 --- a/src/core/migrations/0009_auto_20170925_1133.py +++ b/src/core/migrations/0009_auto_20170925_1133.py @@ -8,7 +8,7 @@ def fix_uuids(apps, schema_editor): - Account = apps.get_model('core', 'Account') + Account = apps.get_model("core", "Account") for account in Account.objects.all(): account.uuid = uuid.uuid4() @@ -16,9 +16,8 @@ def fix_uuids(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0008_auto_20170925_1129'), + ("core", "0008_auto_20170925_1129"), ] operations = [ diff --git a/src/core/migrations/0010_account_enable_public_profile.py b/src/core/migrations/0010_account_enable_public_profile.py index ab7daab2b0..44610b839f 100755 --- a/src/core/migrations/0010_account_enable_public_profile.py +++ b/src/core/migrations/0010_account_enable_public_profile.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0009_auto_20170925_1133'), + ("core", "0009_auto_20170925_1133"), ] operations = [ migrations.AddField( - model_name='account', - name='enable_public_profile', - field=models.BooleanField(default=False, help_text='If enabled, your basic profile will be available to the public.'), + model_name="account", + name="enable_public_profile", + field=models.BooleanField( + default=False, + help_text="If enabled, your basic profile will be available to the public.", + ), ), ] diff --git a/src/core/migrations/0011_auto_20171010_1535.py b/src/core/migrations/0011_auto_20171010_1535.py index d4f432c364..0c04c97e87 100755 --- a/src/core/migrations/0011_auto_20171010_1535.py +++ b/src/core/migrations/0011_auto_20171010_1535.py @@ -7,15 +7,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0010_account_enable_public_profile'), + ("core", "0010_account_enable_public_profile"), ] operations = [ migrations.AlterField( - model_name='galley', - name='article', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="galley", + name="article", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), ), ] diff --git a/src/core/migrations/0012_workflowlog.py b/src/core/migrations/0012_workflowlog.py index 1acfa896e8..3b5e1f9f6c 100644 --- a/src/core/migrations/0012_workflowlog.py +++ b/src/core/migrations/0012_workflowlog.py @@ -8,20 +8,39 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), - ('core', '0011_auto_20171010_1535'), + ("submission", "0019_auto_20171130_1115"), + ("core", "0011_auto_20171010_1535"), ] operations = [ migrations.CreateModel( - name='WorkflowLog', + name="WorkflowLog", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('element', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.WorkflowElement')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("timestamp", models.DateTimeField(default=django.utils.timezone.now)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "element", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="core.WorkflowElement", + ), + ), ], ), ] diff --git a/src/core/migrations/0013_auto_20180207_1525.py b/src/core/migrations/0013_auto_20180207_1525.py index 90636b5247..1cd92d8074 100644 --- a/src/core/migrations/0013_auto_20180207_1525.py +++ b/src/core/migrations/0013_auto_20180207_1525.py @@ -6,23 +6,22 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0012_workflowlog'), + ("core", "0012_workflowlog"), ] operations = [ migrations.AlterModelOptions( - name='account', - options={'ordering': ('first_name', 'last_name', 'username')}, + name="account", + options={"ordering": ("first_name", "last_name", "username")}, ), migrations.AlterModelOptions( - name='workflowlog', - options={'ordering': ('timestamp',)}, + name="workflowlog", + options={"ordering": ("timestamp",)}, ), migrations.AddField( - model_name='workflowelement', - name='article_url', + model_name="workflowelement", + name="article_url", field=models.BooleanField(default=True), ), ] diff --git a/src/core/migrations/0014_workflowelement_jump_url.py b/src/core/migrations/0014_workflowelement_jump_url.py index 54bc3d78ef..6888925d52 100644 --- a/src/core/migrations/0014_workflowelement_jump_url.py +++ b/src/core/migrations/0014_workflowelement_jump_url.py @@ -6,16 +6,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0013_auto_20180207_1525'), + ("core", "0013_auto_20180207_1525"), ] operations = [ migrations.AddField( - model_name='workflowelement', - name='jump_url', - field=models.CharField(default='core_dashboard', max_length=255), + model_name="workflowelement", + name="jump_url", + field=models.CharField(default="core_dashboard", max_length=255), preserve_default=False, ), ] diff --git a/src/core/migrations/0015_supplementaryfile.py b/src/core/migrations/0015_supplementaryfile.py index 7e9a375a77..a15ddd2cfb 100644 --- a/src/core/migrations/0015_supplementaryfile.py +++ b/src/core/migrations/0015_supplementaryfile.py @@ -7,18 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0013_auto_20180207_1525'), + ("core", "0013_auto_20180207_1525"), ] operations = [ migrations.CreateModel( - name='SupplementaryFile', + name="SupplementaryFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('doi', models.CharField(max_length=255)), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.File')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("doi", models.CharField(max_length=255)), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.File" + ), + ), ], ), ] diff --git a/src/core/migrations/0016_merge_20180417_1131.py b/src/core/migrations/0016_merge_20180417_1131.py index f16744c6ef..ca157e3870 100644 --- a/src/core/migrations/0016_merge_20180417_1131.py +++ b/src/core/migrations/0016_merge_20180417_1131.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0015_supplementaryfile'), - ('core', '0014_workflowelement_jump_url'), + ("core", "0015_supplementaryfile"), + ("core", "0014_workflowelement_jump_url"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0017_auto_20180417_1254.py b/src/core/migrations/0017_auto_20180417_1254.py index 5207dee2d2..a11d13e116 100644 --- a/src/core/migrations/0017_auto_20180417_1254.py +++ b/src/core/migrations/0017_auto_20180417_1254.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0016_merge_20180417_1131'), + ("core", "0016_merge_20180417_1131"), ] operations = [ migrations.AlterField( - model_name='supplementaryfile', - name='doi', + model_name="supplementaryfile", + name="doi", field=models.CharField(blank=True, max_length=255, null=True), ), ] diff --git a/src/core/migrations/0018_auto_20181116_1123.py b/src/core/migrations/0018_auto_20181116_1123.py index eff88fda9c..29563d5e3f 100644 --- a/src/core/migrations/0018_auto_20181116_1123.py +++ b/src/core/migrations/0018_auto_20181116_1123.py @@ -8,15 +8,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0017_auto_20180417_1254'), + ("core", "0017_auto_20180417_1254"), ] operations = [ migrations.AlterField( - model_name='account', - name='profile_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=core.models.profile_images_upload_path), + model_name="account", + name="profile_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=core.models.profile_images_upload_path, + ), ), ] diff --git a/src/core/migrations/0019_adds_DomainAlias_journal_FK.py b/src/core/migrations/0019_adds_DomainAlias_journal_FK.py index e355134d56..03ca749675 100644 --- a/src/core/migrations/0019_adds_DomainAlias_journal_FK.py +++ b/src/core/migrations/0019_adds_DomainAlias_journal_FK.py @@ -33,36 +33,43 @@ def sitesectomy(*args, **kwargs): # New installations won't have a django_sites table pass + class Migration(migrations.Migration): atomic = False dependencies = [ - ('journal', '0017_file_fields_for_the_last_time'), - ('core', '0018_auto_20181116_1123'), + ("journal", "0017_file_fields_for_the_last_time"), + ("core", "0018_auto_20181116_1123"), ] operations = [ migrations.AddField( - model_name='domainalias', - name='press', + model_name="domainalias", + name="press", field=models.ForeignKey( blank=True, null=True, - on_delete=django.db.models.deletion.CASCADE, to='press.Press'), + on_delete=django.db.models.deletion.CASCADE, + to="press.Press", + ), ), migrations.AlterField( - model_name='domainalias', - name='domain', - field=models.CharField(default='www.example.com', max_length=255, unique=True), + model_name="domainalias", + name="domain", + field=models.CharField( + default="www.example.com", max_length=255, unique=True + ), ), migrations.AddField( - model_name='domainalias', - name='journal', + model_name="domainalias", + name="journal", field=models.ForeignKey( blank=True, null=True, - on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), migrations.RunPython(sitesectomy, reverse_code=migrations.RunPython.noop), -# migrations.RunSQL(JOURNAL_SITESECTOMY, reverse_sql=migrations.RunSQL.noop), -# migrations.RunSQL(PRESS_SITESECTOMY, reverse_sql=migrations.RunSQL.noop), + # migrations.RunSQL(JOURNAL_SITESECTOMY, reverse_sql=migrations.RunSQL.noop), + # migrations.RunSQL(PRESS_SITESECTOMY, reverse_sql=migrations.RunSQL.noop), ] diff --git a/src/core/migrations/0019_set_default_news_items.py b/src/core/migrations/0019_set_default_news_items.py index 7330e99044..352be721f3 100644 --- a/src/core/migrations/0019_set_default_news_items.py +++ b/src/core/migrations/0019_set_default_news_items.py @@ -16,15 +16,12 @@ def set_default_news_items(apps, schema_editor): else: journals = Journal.objects.all() for journal in journals: - plugin_setting, c = PluginSetting.objects.get_or_create( - plugin=plugin, - name="number_of_articles" + plugin=plugin, name="number_of_articles" ) plugin_setting_value, c = PluginSettingValue.objects.get_or_create( - setting=plugin_setting, - journal=journal + setting=plugin_setting, journal=journal ) SQL = """ @@ -37,20 +34,14 @@ def set_default_news_items(apps, schema_editor): cursor.execute(SQL) - - - - class Migration(migrations.Migration): - dependencies = [ - ('core', '0018_auto_20181116_1123'), - ('utils', '0009_auto_20180808_1514'), + ("core", "0018_auto_20181116_1123"), + ("utils", "0009_auto_20180808_1514"), ] operations = [ migrations.RunPython( - set_default_news_items, - reverse_code=migrations.RunPython.noop + set_default_news_items, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/core/migrations/0020_removes_DomainAlias_site_id.py b/src/core/migrations/0020_removes_DomainAlias_site_id.py index 5a7382b13f..25efd6c587 100644 --- a/src/core/migrations/0020_removes_DomainAlias_site_id.py +++ b/src/core/migrations/0020_removes_DomainAlias_site_id.py @@ -7,15 +7,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0017_file_fields_for_the_last_time'), - ('core', '0019_adds_DomainAlias_journal_FK'), + ("journal", "0017_file_fields_for_the_last_time"), + ("core", "0019_adds_DomainAlias_journal_FK"), ] operations = [ migrations.RemoveField( - model_name='domainalias', - name='site_id', + model_name="domainalias", + name="site_id", ), ] diff --git a/src/core/migrations/0021_domainalias_is_secure.py b/src/core/migrations/0021_domainalias_is_secure.py index 4e0911a063..5b113032ca 100644 --- a/src/core/migrations/0021_domainalias_is_secure.py +++ b/src/core/migrations/0021_domainalias_is_secure.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0020_removes_DomainAlias_site_id'), + ("core", "0020_removes_DomainAlias_site_id"), ] operations = [ migrations.AddField( - model_name='domainalias', - name='is_secure', - field=models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.'), + model_name="domainalias", + name="is_secure", + field=models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), ), ] diff --git a/src/core/migrations/0022_merge_20181219_1312.py b/src/core/migrations/0022_merge_20181219_1312.py index 4bb9b686c9..3e7ee373b4 100644 --- a/src/core/migrations/0022_merge_20181219_1312.py +++ b/src/core/migrations/0022_merge_20181219_1312.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0021_domainalias_is_secure'), - ('core', '0019_set_default_news_items'), + ("core", "0021_domainalias_is_secure"), + ("core", "0019_set_default_news_items"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0023_fix_journal_base_url_from_settings.py b/src/core/migrations/0023_fix_journal_base_url_from_settings.py index 7dee582ce9..6ebd2762f2 100755 --- a/src/core/migrations/0023_fix_journal_base_url_from_settings.py +++ b/src/core/migrations/0023_fix_journal_base_url_from_settings.py @@ -3,25 +3,32 @@ def replace_journal_base_url(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.all() for setting in settings: - setting.value = setting.value.replace("request.journal_base_url", "journal.site_url") + setting.value = setting.value.replace( + "request.journal_base_url", "journal.site_url" + ) setting.save() + def reverse_code(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.all() for setting in settings: - setting.value = setting.value.replace("journal.site_url", "request.replace_journal_base_url") + setting.value = setting.value.replace( + "journal.site_url", "request.replace_journal_base_url" + ) setting.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0022_merge_20181219_1312'), + ("core", "0022_merge_20181219_1312"), ] operations = [ - migrations.RunPython(replace_journal_base_url, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_journal_base_url, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0024_auto_20190129_1647.py b/src/core/migrations/0024_auto_20190129_1647.py index b37318712c..40ff416dab 100644 --- a/src/core/migrations/0024_auto_20190129_1647.py +++ b/src/core/migrations/0024_auto_20190129_1647.py @@ -6,20 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0023_fix_journal_base_url_from_settings'), + ("core", "0023_fix_journal_base_url_from_settings"), ] operations = [ migrations.AlterField( - model_name='account', - name='first_name', - field=models.CharField(max_length=300, null=True, verbose_name='First name'), + model_name="account", + name="first_name", + field=models.CharField( + max_length=300, null=True, verbose_name="First name" + ), ), migrations.AlterField( - model_name='account', - name='last_name', - field=models.CharField(max_length=300, null=True, verbose_name='Last name'), + model_name="account", + name="last_name", + field=models.CharField(max_length=300, null=True, verbose_name="Last name"), ), ] diff --git a/src/core/migrations/0024_auto_20190201_1015.py b/src/core/migrations/0024_auto_20190201_1015.py index 1ae9c9cab1..e19c012a14 100644 --- a/src/core/migrations/0024_auto_20190201_1015.py +++ b/src/core/migrations/0024_auto_20190201_1015.py @@ -6,21 +6,21 @@ def fix_featured_articles(apps, schema_editor): - HomepageElement = apps.get_model('core', 'HomepageElement') - elements = HomepageElement.objects.filter(name='Featured Articles') + HomepageElement = apps.get_model("core", "HomepageElement") + elements = HomepageElement.objects.filter(name="Featured Articles") for element in elements: - element.configure_url = 'featured_articles_setup' + element.configure_url = "featured_articles_setup" element.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0023_fix_journal_base_url_from_settings'), + ("core", "0023_fix_journal_base_url_from_settings"), ] operations = [ - migrations.RunPython(fix_featured_articles, - reverse_code=migrations.RunPython.noop), + migrations.RunPython( + fix_featured_articles, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0025_fix_bad_journal_urls_in_template.py b/src/core/migrations/0025_fix_bad_journal_urls_in_template.py index 9c94aa39eb..99023a1298 100755 --- a/src/core/migrations/0025_fix_bad_journal_urls_in_template.py +++ b/src/core/migrations/0025_fix_bad_journal_urls_in_template.py @@ -6,22 +6,24 @@ REGEX = re.compile("({{\ ?journal.site_url\ ?}})?{% url '(\w+)' ([\w\ \.]*)%}") OUTPUT_FMT = "{%% journal_url '%s' %s%%}" -def replace_matches( match): + +def replace_matches(match): view_name = match.group(2) args = match.group(3) return OUTPUT_FMT % (view_name, args) + def replace_bad_urls(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.all() for setting in settings: setting.value = re.sub(REGEX, replace_matches, setting.value) setting.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0024_auto_20190201_1015'), + ("core", "0024_auto_20190201_1015"), ] operations = [ diff --git a/src/core/migrations/0025_merge_20190201_1533.py b/src/core/migrations/0025_merge_20190201_1533.py index 2bba73cbc5..c66d276e52 100644 --- a/src/core/migrations/0025_merge_20190201_1533.py +++ b/src/core/migrations/0025_merge_20190201_1533.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0024_auto_20190201_1015'), - ('core', '0024_auto_20190129_1647'), + ("core", "0024_auto_20190201_1015"), + ("core", "0024_auto_20190129_1647"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0026_merge_20190212_1718.py b/src/core/migrations/0026_merge_20190212_1718.py index bc4b6291cd..1d5b18dd08 100644 --- a/src/core/migrations/0026_merge_20190212_1718.py +++ b/src/core/migrations/0026_merge_20190212_1718.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0025_merge_20190201_1533'), - ('core', '0025_fix_bad_journal_urls_in_template'), + ("core", "0025_merge_20190201_1533"), + ("core", "0025_fix_bad_journal_urls_in_template"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0027_account_preferred_timezone.py b/src/core/migrations/0027_account_preferred_timezone.py index dad5083301..be2b64a637 100644 --- a/src/core/migrations/0027_account_preferred_timezone.py +++ b/src/core/migrations/0027_account_preferred_timezone.py @@ -6,15 +6,624 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0026_merge_20190212_1718'), + ("core", "0026_merge_20190212_1718"), ] operations = [ migrations.AddField( - model_name='account', - name='preferred_timezone', - field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], max_length=300, null=True), + model_name="account", + name="preferred_timezone", + field=models.CharField( + blank=True, + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + max_length=300, + null=True, + ), ), ] diff --git a/src/core/migrations/0028_fix_bad_email_urls.py b/src/core/migrations/0028_fix_bad_email_urls.py index 25991b1d2e..f495122714 100644 --- a/src/core/migrations/0028_fix_bad_email_urls.py +++ b/src/core/migrations/0028_fix_bad_email_urls.py @@ -6,22 +6,24 @@ REGEX = re.compile("({{\ ?request.journal.site_url\ ?}}){% url '(\w+)' ([\w\ \.]*)%}") OUTPUT_FMT = "{%% journal_url '%s' %s%%}" -def replace_matches( match): + +def replace_matches(match): view_name = match.group(2) args = match.group(3) return OUTPUT_FMT % (view_name, args) + def replace_bad_urls(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.all() for setting in settings: setting.value = re.sub(REGEX, replace_matches, setting.value) setting.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0027_account_preferred_timezone'), + ("core", "0027_account_preferred_timezone"), ] operations = [ diff --git a/src/core/migrations/0029_adds_default_case_for_setting_value.py b/src/core/migrations/0029_adds_default_case_for_setting_value.py index 5da44b8182..69f60a23bc 100644 --- a/src/core/migrations/0029_adds_default_case_for_setting_value.py +++ b/src/core/migrations/0029_adds_default_case_for_setting_value.py @@ -7,24 +7,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0024_auto_20190304_0916'), - ('core', '0028_fix_bad_email_urls'), + ("journal", "0024_auto_20190304_0916"), + ("core", "0028_fix_bad_email_urls"), ] operations = [ migrations.AlterModelOptions( - name='settingvalue', + name="settingvalue", options={}, ), migrations.AlterField( - model_name='settingvalue', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="settingvalue", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), migrations.AlterUniqueTogether( - name='settingvalue', - unique_together=set([('journal', 'setting')]), + name="settingvalue", + unique_together=set([("journal", "setting")]), ), ] diff --git a/src/core/migrations/0029_rename_setting.py b/src/core/migrations/0029_rename_setting.py index 1b8a09fd0a..ca77767959 100644 --- a/src/core/migrations/0029_rename_setting.py +++ b/src/core/migrations/0029_rename_setting.py @@ -6,30 +6,30 @@ def replace_setting_name(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - name='mulit_page_editorial', - group__name='general', - ).update(name='multi_page_editorial') + name="mulit_page_editorial", + group__name="general", + ).update(name="multi_page_editorial") def undo_replace_setting_name(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - name='multi_page_editorial', - group__name='general', - ).update(name='mulit_page_editorial') + name="multi_page_editorial", + group__name="general", + ).update(name="mulit_page_editorial") class Migration(migrations.Migration): - dependencies = [ - ('core', '0028_fix_bad_email_urls'), + ("core", "0028_fix_bad_email_urls"), ] operations = [ - migrations.RunPython(replace_setting_name, - reverse_code=undo_replace_setting_name), + migrations.RunPython( + replace_setting_name, reverse_code=undo_replace_setting_name + ), ] diff --git a/src/core/migrations/0030_merge_20190405_1549.py b/src/core/migrations/0030_merge_20190405_1549.py index d6b3f4b490..eb57f60b82 100644 --- a/src/core/migrations/0030_merge_20190405_1549.py +++ b/src/core/migrations/0030_merge_20190405_1549.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0029_adds_default_case_for_setting_value'), - ('core', '0029_rename_setting'), + ("core", "0029_adds_default_case_for_setting_value"), + ("core", "0029_rename_setting"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0031_auto_20191103_2035.py b/src/core/migrations/0031_auto_20191103_2035.py index a86bcb70bc..4018b3aa29 100644 --- a/src/core/migrations/0031_auto_20191103_2035.py +++ b/src/core/migrations/0031_auto_20191103_2035.py @@ -9,25 +9,56 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0030_merge_20190405_1549'), + ("core", "0030_merge_20190405_1549"), ] operations = [ migrations.CreateModel( - name='XSLFile', + name="XSLFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.FileField(storage=core.file_system.JanewayFileSystemStorage('transform/xsl'), upload_to='')), - ('date_uploaded', models.DateTimeField(default=django.utils.timezone.now)), - ('label', models.CharField(help_text='A label to help recognise this stylesheet', max_length=255, unique=True)), - ('comments', models.TextField(blank=True, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "file", + models.FileField( + storage=core.file_system.JanewayFileSystemStorage( + "transform/xsl" + ), + upload_to="", + ), + ), + ( + "date_uploaded", + models.DateTimeField(default=django.utils.timezone.now), + ), + ( + "label", + models.CharField( + help_text="A label to help recognise this stylesheet", + max_length=255, + unique=True, + ), + ), + ("comments", models.TextField(blank=True, null=True)), ], ), migrations.AddField( - model_name='galley', - name='xsl_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='xsl_file', to='core.XSLFile'), + model_name="galley", + name="xsl_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="xsl_file", + to="core.XSLFile", + ), ), ] diff --git a/src/core/migrations/0032_install_xsl_files.py b/src/core/migrations/0032_install_xsl_files.py index 6910171266..b47ea02c7d 100644 --- a/src/core/migrations/0032_install_xsl_files.py +++ b/src/core/migrations/0032_install_xsl_files.py @@ -7,20 +7,16 @@ def install_xsl_files(apps, schema_editor): default_xsl_path = os.path.join(settings.BASE_DIR, "transform/xsl/default.xsl") - install_xsl_file( - apps, - label=settings.DEFAULT_XSL_FILE_LABEL, - path=default_xsl_path - ) + install_xsl_file(apps, label=settings.DEFAULT_XSL_FILE_LABEL, path=default_xsl_path) def install_xsl_file(apps, label, path=None, file_=None): - XSLFile = apps.get_model('core', 'XSLFile') + XSLFile = apps.get_model("core", "XSLFile") if XSLFile.objects.filter(label=label).exists(): return xsl_file = XSLFile(label=label) if path: - with open(path, 'r', encoding="utf-8") as f: + with open(path, "r", encoding="utf-8") as f: file_ = ContentFile(f.read()) file_.name = "default.xsl" xsl_file.file = file_ @@ -33,9 +29,8 @@ def install_xsl_file(apps, label, path=None, file_=None): class Migration(migrations.Migration): - dependencies = [ - ('core', '0031_auto_20191103_2035'), + ("core", "0031_auto_20191103_2035"), ] operations = [ diff --git a/src/core/migrations/0033_set_default_xml_galley_xsl.py b/src/core/migrations/0033_set_default_xml_galley_xsl.py index 1413ac6885..552925fafa 100644 --- a/src/core/migrations/0033_set_default_xml_galley_xsl.py +++ b/src/core/migrations/0033_set_default_xml_galley_xsl.py @@ -3,19 +3,19 @@ from django.db import migrations from django.conf import settings + def set_galley_xsl(apps, schema_editor): Galley = apps.get_model("core", "Galley") XSLFile = apps.get_model("core", "XSLFile") xsl_file = XSLFile.objects.get(label=settings.DEFAULT_XSL_FILE_LABEL) - for galley in Galley.objects.filter(label='XML'): + for galley in Galley.objects.filter(label="XML"): galley.xsl_file = xsl_file galley.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0032_install_xsl_files'), + ("core", "0032_install_xsl_files"), ] operations = [ diff --git a/src/core/migrations/0034_fix_wrong_signature_review_ack.py b/src/core/migrations/0034_fix_wrong_signature_review_ack.py index 1f34a6b806..e455e63cba 100755 --- a/src/core/migrations/0034_fix_wrong_signature_review_ack.py +++ b/src/core/migrations/0034_fix_wrong_signature_review_ack.py @@ -8,24 +8,25 @@ def replace_template(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.filter(value=OLD_VALUE) for setting in settings: setting.value = NEW_VALUE setting.save() + def reverse_code(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.filter(value=NEW_VALUE) for setting in settings: setting.value = OLD_VALUE setting.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0033_set_default_xml_galley_xsl'), + ("core", "0033_set_default_xml_galley_xsl"), ] operations = [ diff --git a/src/core/migrations/0034_update_handshake_urls.py b/src/core/migrations/0034_update_handshake_urls.py index 19bbefe319..fcf5da0e62 100644 --- a/src/core/migrations/0034_update_handshake_urls.py +++ b/src/core/migrations/0034_update_handshake_urls.py @@ -5,20 +5,21 @@ from django.db import migrations updates = [ - {'from': 'review_unassigned_article', 'to': 'review_home'}, - {'from': 'publish_article', 'to': 'publish'}, - {'from': 'article_copyediting', 'to': 'copyediting'}, + {"from": "review_unassigned_article", "to": "review_home"}, + {"from": "publish_article", "to": "publish"}, + {"from": "article_copyediting", "to": "copyediting"}, ] + def update_handshake_urls(apps, schema_editor): WorkflowElement = apps.get_model("core", "WorkflowElement") for update in updates: elements_to_update = WorkflowElement.objects.filter( - handshake_url=update.get('from') + handshake_url=update.get("from") ) for element_to_update in elements_to_update: - element_to_update.handshake_url = update.get('to') + element_to_update.handshake_url = update.get("to") element_to_update.save() @@ -27,17 +28,16 @@ def reverse_handshake_urls(apps, schema_editor): for update in updates: elements_to_update = WorkflowElement.objects.filter( - handshake_url=update.get('to') + handshake_url=update.get("to") ) for element_to_update in elements_to_update: - element_to_update.handshake_url = update.get('from') + element_to_update.handshake_url = update.get("from") element_to_update.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0033_set_default_xml_galley_xsl'), + ("core", "0033_set_default_xml_galley_xsl"), ] operations = [ diff --git a/src/core/migrations/0035_update_revision_assignment_email.py b/src/core/migrations/0035_update_revision_assignment_email.py index 1509342e39..b300ba8b5f 100644 --- a/src/core/migrations/0035_update_revision_assignment_email.py +++ b/src/core/migrations/0035_update_revision_assignment_email.py @@ -9,7 +9,7 @@ def replace_template(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.filter(value=OLD_VALUE) for setting in settings: @@ -18,7 +18,7 @@ def replace_template(apps, schema_editor): def reverse_code(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.filter(value=NEW_VALUE) for setting in settings: setting.value = OLD_VALUE @@ -26,9 +26,8 @@ def reverse_code(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0034_fix_wrong_signature_review_ack'), + ("core", "0034_fix_wrong_signature_review_ack"), ] operations = [ diff --git a/src/core/migrations/0036_merge_20200420_1333.py b/src/core/migrations/0036_merge_20200420_1333.py index 17a5f17937..7d95c6cc76 100644 --- a/src/core/migrations/0036_merge_20200420_1333.py +++ b/src/core/migrations/0036_merge_20200420_1333.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0035_update_revision_assignment_email'), - ('core', '0034_update_handshake_urls'), + ("core", "0035_update_revision_assignment_email"), + ("core", "0034_update_handshake_urls"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0037_journal_xsl_files.py b/src/core/migrations/0037_journal_xsl_files.py index d1c2f04919..238538820a 100644 --- a/src/core/migrations/0037_journal_xsl_files.py +++ b/src/core/migrations/0037_journal_xsl_files.py @@ -22,23 +22,18 @@ def move_xsl_files(apps, schema_editor, from_, to): if not filename: continue elif filename == "default.xsl": - shutil.copyfile( - os.path.join(from_, filename), - os.path.join(to, filename) - ) + shutil.copyfile(os.path.join(from_, filename), os.path.join(to, filename)) else: try: - shutil.move( - os.path.join(from_, filename), - os.path.join(to, filename) - ) + shutil.move(os.path.join(from_, filename), os.path.join(to, filename)) except FileNotFoundError: pass def move_xsl_to_new(apps, schema_editor, *args, **kwargs): move_xsl_files( - apps, schema_editor, + apps, + schema_editor, from_=os.path.join(settings.BASE_DIR, "transform/xsl"), to=os.path.join(settings.BASE_DIR, "files/xsl"), ) @@ -46,34 +41,42 @@ def move_xsl_to_new(apps, schema_editor, *args, **kwargs): def move_xsl_to_old(apps, schema_editor, *args, **kwargs): move_xsl_files( - apps, schema_editor, + apps, + schema_editor, from_=os.path.join(settings.BASE_DIR, "files/xsl"), to=os.path.join(settings.BASE_DIR, "transform/xsl"), ) class Migration(migrations.Migration): - dependencies = [ - ('core', '0036_merge_20200420_1333'), + ("core", "0036_merge_20200420_1333"), ] operations = [ migrations.AddField( - model_name='xslfile', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="xslfile", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), migrations.AddField( - model_name='xslfile', - name='original_filename', - field=models.CharField(default='default.xsl', max_length=255), + model_name="xslfile", + name="original_filename", + field=models.CharField(default="default.xsl", max_length=255), preserve_default=False, ), migrations.AlterField( - model_name='xslfile', - name='file', - field=models.FileField(storage=core.file_system.JanewayFileSystemStorage('files/xsl'), upload_to=core.models.upload_to_journal), + model_name="xslfile", + name="file", + field=models.FileField( + storage=core.file_system.JanewayFileSystemStorage("files/xsl"), + upload_to=core.models.upload_to_journal, + ), ), - migrations.RunPython(move_xsl_to_new, reverse_code=move_xsl_to_old) + migrations.RunPython(move_xsl_to_new, reverse_code=move_xsl_to_old), ] diff --git a/src/core/migrations/0038_xslt_1-3-8.py b/src/core/migrations/0038_xslt_1-3-8.py index 5078441276..75130e1318 100644 --- a/src/core/migrations/0038_xslt_1-3-8.py +++ b/src/core/migrations/0038_xslt_1-3-8.py @@ -19,7 +19,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -34,7 +34,7 @@ def upgrade(apps, schema_editor): return else: xsl_path = os.path.join(settings.BASE_DIR, "transform/xsl/default.xsl") - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = "default.xsl" @@ -55,11 +55,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0037_journal_xsl_files'), + ("core", "0037_journal_xsl_files"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0039_fix_reviewer_url.py b/src/core/migrations/0039_fix_reviewer_url.py index 6af08592f4..8edb4da43b 100644 --- a/src/core/migrations/0039_fix_reviewer_url.py +++ b/src/core/migrations/0039_fix_reviewer_url.py @@ -8,8 +8,9 @@ REGEX = re.compile("({%\ ?journal_url 'do_review' review_assignment.id\ ?%})") OUTPUT = "{{ review_url }}" + def replace_template(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") settings = SettingValueTranslation.objects.all() for setting in settings: @@ -19,9 +20,8 @@ def replace_template(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0038_xslt_1-3-8'), + ("core", "0038_xslt_1-3-8"), ] operations = [ diff --git a/src/core/migrations/0040_auto_20200529_1415.py b/src/core/migrations/0040_auto_20200529_1415.py index 3cc0248f77..86f0ae5a8f 100644 --- a/src/core/migrations/0040_auto_20200529_1415.py +++ b/src/core/migrations/0040_auto_20200529_1415.py @@ -6,19 +6,33 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0039_fix_reviewer_url'), + ("core", "0039_fix_reviewer_url"), ] operations = [ migrations.AlterField( - model_name='galley', - name='type', - field=models.CharField(choices=[('pdf', 'PDF'), ('epub', 'EPUB'), ('html', 'HTML'), ('xml', 'XML'), ('doc', 'Word (Doc)'), ('docx', 'Word (DOCX)'), ('odt', 'OpenDocument Text Document'), ('tex', 'LaTeX'), ('rtf', 'RTF'), ('other', 'Other'), ('image', 'Image')], max_length=100), + model_name="galley", + name="type", + field=models.CharField( + choices=[ + ("pdf", "PDF"), + ("epub", "EPUB"), + ("html", "HTML"), + ("xml", "XML"), + ("doc", "Word (Doc)"), + ("docx", "Word (DOCX)"), + ("odt", "OpenDocument Text Document"), + ("tex", "LaTeX"), + ("rtf", "RTF"), + ("other", "Other"), + ("image", "Image"), + ], + max_length=100, + ), ), migrations.AlterUniqueTogether( - name='account', - unique_together=set([('email', 'username')]), + name="account", + unique_together=set([("email", "username")]), ), ] diff --git a/src/core/migrations/0041_fix_reminder_name_description.py b/src/core/migrations/0041_fix_reminder_name_description.py index 6e4c4dcb3f..3ccb64fdf6 100644 --- a/src/core/migrations/0041_fix_reminder_name_description.py +++ b/src/core/migrations/0041_fix_reminder_name_description.py @@ -7,60 +7,61 @@ EMAILS_TO_FIX = [ { - 'name': 'accepted_review_reminder', - 'pretty_name': 'Accepted Review Reminder', - 'description': 'Notify reviewer of accepted request and remind them to review submission.', + "name": "accepted_review_reminder", + "pretty_name": "Accepted Review Reminder", + "description": "Notify reviewer of accepted request and remind them to review submission.", }, { - 'name': 'author_publication', - 'pretty_name': 'Author Publication Notification', - 'description': 'Notify the author of their publication date and time.', + "name": "author_publication", + "pretty_name": "Author Publication Notification", + "description": "Notify the author of their publication date and time.", }, { - 'name': 'review_decline_acknowledgement', - 'pretty_name': 'Review Decline Acknowledgement', + "name": "review_decline_acknowledgement", + "pretty_name": "Review Decline Acknowledgement", }, { - 'name': 'editor_digest', - 'description': 'Digest email sent to editors.', + "name": "editor_digest", + "description": "Digest email sent to editors.", }, { - 'name': 'notify_proofreader_assignment', - 'description': 'Email sent to a proofreader when they are given an assignment.' + "name": "notify_proofreader_assignment", + "description": "Email sent to a proofreader when they are given an assignment.", }, { - 'name': 'review_request_sent', - 'pretty_name': 'Review Request Update', - 'description': 'Email sent when a review assignment is updated.', + "name": "review_request_sent", + "pretty_name": "Review Request Update", + "description": "Email sent when a review assignment is updated.", }, ] + def fix_email_names_descriptions(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") for fix in EMAILS_TO_FIX: - settings = Setting.objects.filter(name=fix.get('name')) + settings = Setting.objects.filter(name=fix.get("name")) for setting in settings: - if fix.get('pretty_name'): - setting.pretty_name = fix.get('pretty_name') + if fix.get("pretty_name"): + setting.pretty_name = fix.get("pretty_name") - if fix.get('description'): - setting.description = fix.get('description') + if fix.get("description"): + setting.description = fix.get("description") setting.save() def move_from_address_to_general(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') - Group = apps.get_model('core', 'SettingGroup') + Setting = apps.get_model("core", "Setting") + Group = apps.get_model("core", "SettingGroup") try: - group = Group.objects.get(name='general') + group = Group.objects.get(name="general") try: from_address = Setting.objects.get( - name='from_address', + name="from_address", ) from_address.group = group from_address.save() @@ -68,15 +69,15 @@ def move_from_address_to_general(apps, schema_editor): # In this case load_default_settings has been run before this migration. # We need to delete that new setting. Setting.objects.get( - name='from_address', - group__name='general', + name="from_address", + group__name="general", ).delete() # And the update the existing one so that any linked SettingValues are # retained. setting_to_retain = Setting.objects.get( - name='from_address', - group__name='email', + name="from_address", + group__name="email", ) setting_to_retain.group = group setting_to_retain.save() @@ -86,21 +87,17 @@ def move_from_address_to_general(apps, schema_editor): def move_from_address_to_email(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') - Group = apps.get_model('core', 'SettingGroup') + Setting = apps.get_model("core", "Setting") + Group = apps.get_model("core", "SettingGroup") - group = Group.objects.get(name='email') + group = Group.objects.get(name="email") - Setting.objects.filter( - name='from_address' - ).update( - group=group - ) + Setting.objects.filter(name="from_address").update(group=group) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0040_auto_20200529_1415'), + ("core", "0040_auto_20200529_1415"), ] operations = [ @@ -111,5 +108,5 @@ class Migration(migrations.Migration): migrations.RunPython( move_from_address_to_general, reverse_code=move_from_address_to_email, - ) + ), ] diff --git a/src/core/migrations/0042_auto_20200825_1051.py b/src/core/migrations/0042_auto_20200825_1051.py index 980f8e1a14..d954345fc8 100644 --- a/src/core/migrations/0042_auto_20200825_1051.py +++ b/src/core/migrations/0042_auto_20200825_1051.py @@ -8,7 +8,7 @@ def lower_all_usernames(apps, schema_editor): - Account = apps.get_model('core', 'Account') + Account = apps.get_model("core", "Account") accounts = Account.objects.all() handled = set() @@ -23,9 +23,10 @@ def lower_all_usernames(apps, schema_editor): same_accounts = Account.objects.filter(username__iexact=account.username) handled |= handle_unique_username_violation(same_accounts, apps) + def handle_unique_username_violation(same_accounts, apps): - AccountRole = apps.get_model('core', 'Accountrole') - Account = apps.get_model('core', 'Account') + AccountRole = apps.get_model("core", "Accountrole") + Account = apps.get_model("core", "Account") real_account = None # Try checking if one has logged in @@ -35,11 +36,13 @@ def handle_unique_username_violation(same_accounts, apps): if real_account is None: # Try checking if one account has a non-author role: - account_roles_ids = AccountRole.objects.filter( - user__in=same_accounts, - ).exclude( - role__slug="author" - ).values_list("user", flat=True) + account_roles_ids = ( + AccountRole.objects.filter( + user__in=same_accounts, + ) + .exclude(role__slug="author") + .values_list("user", flat=True) + ) if len(set(account_roles_ids)) == 1: real_account = Account.objects.get(id=account_roles_ids[0]) @@ -49,9 +52,10 @@ def handle_unique_username_violation(same_accounts, apps): for acc in same_accounts: if acc.article_set.exists(): authors.append(acc) - if len(authors) >2: + if len(authors) > 2: raise Exception( - "Can't workout the real user for username %s" % acc.username) + "Can't workout the real user for username %s" % acc.username + ) elif len(authors) == 1: real_account = authors[0] @@ -70,14 +74,9 @@ def handle_unique_username_violation(same_accounts, apps): return merged - - - - class Migration(migrations.Migration): - dependencies = [ - ('core', '0041_fix_reminder_name_description'), + ("core", "0041_fix_reminder_name_description"), ] operations = [ diff --git a/src/core/migrations/0043_install_ci_extension_pg.py b/src/core/migrations/0043_install_ci_extension_pg.py index a30b43c053..4f164650dc 100644 --- a/src/core/migrations/0043_install_ci_extension_pg.py +++ b/src/core/migrations/0043_install_ci_extension_pg.py @@ -7,9 +7,8 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0042_auto_20200825_1051'), + ("core", "0042_auto_20200825_1051"), ] operations = [CITextExtension()] diff --git a/src/core/migrations/0044_auto_20201210_1140.py b/src/core/migrations/0044_auto_20201210_1140.py index 6bd043767c..eae8001a6c 100644 --- a/src/core/migrations/0044_auto_20201210_1140.py +++ b/src/core/migrations/0044_auto_20201210_1140.py @@ -7,15 +7,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0043_install_ci_extension_pg'), + ("core", "0043_install_ci_extension_pg"), ] operations = [ migrations.AlterField( - model_name='account', - name='email', - field=core.model_utils.PGCaseInsensitiveEmailField(max_length=254, unique=True, verbose_name='Email'), + model_name="account", + name="email", + field=core.model_utils.PGCaseInsensitiveEmailField( + max_length=254, unique=True, verbose_name="Email" + ), ), ] diff --git a/src/core/migrations/0045_fix_url_emails.py b/src/core/migrations/0045_fix_url_emails.py index dac7491d5a..633f06c5c5 100644 --- a/src/core/migrations/0045_fix_url_emails.py +++ b/src/core/migrations/0045_fix_url_emails.py @@ -10,14 +10,16 @@ def replace_setting_urls(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') - settings = SettingValueTranslation.objects.filter(master__setting__group__name="email") + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") + settings = SettingValueTranslation.objects.filter( + master__setting__group__name="email" + ) for s in settings: fix_url(s) def fix_url(setting): - """ Replaces anchor urls in a setting value with a django variable + """Replaces anchor urls in a setting value with a django variable The django variable is built from the url name, appending "_url" at the end e.g: @@ -57,11 +59,12 @@ def fix_url(setting): class Migration(migrations.Migration): - dependencies = [ - ('core', '0044_auto_20201210_1140'), + ("core", "0044_auto_20201210_1140"), ] operations = [ - migrations.RunPython(replace_setting_urls, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_setting_urls, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0046_delete_review_request_sent.py b/src/core/migrations/0046_delete_review_request_sent.py index ed3cc0a7d3..1356bec022 100644 --- a/src/core/migrations/0046_delete_review_request_sent.py +++ b/src/core/migrations/0046_delete_review_request_sent.py @@ -6,73 +6,72 @@ def delete_settings(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - group__name='email', - name='review_request_sent', + group__name="email", + name="review_request_sent", ).delete() def update_setting_values(apps, schema_editor): - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") prod_complete_settings = SettingValueTranslation.objects.filter( - master__setting__name='production_complete', - master__setting__group__name='email', + master__setting__name="production_complete", + master__setting__group__name="email", ) for setting in prod_complete_settings: setting.value = setting.value.replace( - '

This article is now in the Proofing workflow: {{ proofing_list_url }}.

', - '', + "

This article is now in the Proofing workflow: {{ proofing_list_url }}.

", + "", ) setting.value = setting.value.replace( - 'Dear {{ assignment.editor.full_name }},

This is an automatic', - 'This is a', + "Dear {{ assignment.editor.full_name }},

This is an automatic", + "This is a", ) setting.save() typesetter_notification_settings = SettingValueTranslation.objects.filter( - master__setting__name='typesetter_notification', - master__setting__group__name='email', + master__setting__name="typesetter_notification", + master__setting__group__name="email", ) for setting in typesetter_notification_settings: setting.value = setting.value.replace( - 'This is an automatic', - 'This is a', + "This is an automatic", + "This is a", ) setting.save() notify_editor_proofing_complete_settings = SettingValueTranslation.objects.filter( - master__setting__name='notify_editor_proofing_complete', - master__setting__group__name='email', + master__setting__name="notify_editor_proofing_complete", + master__setting__group__name="email", ) for setting in notify_editor_proofing_complete_settings: setting.value = setting.value.replace( - ': {{ publish_url }}', - '', + ": {{ publish_url }}", + "", ) setting.save() review_ack_settings = SettingValueTranslation.objects.filter( - master__setting__name='review_complete_reviewer_acknowledgement', - master__setting__group__name='email', + master__setting__name="review_complete_reviewer_acknowledgement", + master__setting__group__name="email", ) for setting in review_ack_settings: setting.value = setting.value.replace( - 'request.user.signature', - 'review_assignment.editor.signature', + "request.user.signature", + "review_assignment.editor.signature", ) setting.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0045_fix_url_emails'), + ("core", "0045_fix_url_emails"), ] operations = [ diff --git a/src/core/migrations/0047_hvad_to_modeltranslations.py b/src/core/migrations/0047_hvad_to_modeltranslations.py index ce3a262766..d458d9e837 100644 --- a/src/core/migrations/0047_hvad_to_modeltranslations.py +++ b/src/core/migrations/0047_hvad_to_modeltranslations.py @@ -13,41 +13,40 @@ def check_use_i18n_on(*args, **kwargs): class Migration(migrations.Migration): - dependencies = [ - ('core', '0046_delete_review_request_sent'), + ("core", "0046_delete_review_request_sent"), ] operations = [ migrations.RunPython(check_use_i18n_on, reverse_code=migrations.RunPython.noop), migrations.RenameField( - model_name='settingvaluetranslation', - old_name='value', - new_name='hvad_value', + model_name="settingvaluetranslation", + old_name="value", + new_name="hvad_value", ), migrations.AddField( - model_name='settingvalue', - name='value', + model_name="settingvalue", + name="value", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_cy', + model_name="settingvalue", + name="value_cy", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_de', + model_name="settingvalue", + name="value_de", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_en', + model_name="settingvalue", + name="value_en", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_fr', + model_name="settingvalue", + name="value_fr", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/core/migrations/0047_remove_dots_from_urls.py b/src/core/migrations/0047_remove_dots_from_urls.py index 4fd7392b9c..68a7ab98a4 100644 --- a/src/core/migrations/0047_remove_dots_from_urls.py +++ b/src/core/migrations/0047_remove_dots_from_urls.py @@ -14,14 +14,16 @@ def replace_setting_urls(apps, schema_editor): try: - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') - settings = SettingValueTranslation.objects.filter(master__setting__group__name="email") + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") + settings = SettingValueTranslation.objects.filter( + master__setting__group__name="email" + ) for setting in settings: setting.value = setting.value.replace("url }}.", "url }} ") setting.save() except (LookupError, FieldError): with translation.override(django_settings.LANGUAGE_CODE): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") settings = SettingValue.objects.filter(setting__group__name="email") for setting in settings: if setting.value: @@ -30,11 +32,12 @@ def replace_setting_urls(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0046_delete_review_request_sent'), + ("core", "0046_delete_review_request_sent"), ] operations = [ - migrations.RunPython(replace_setting_urls, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_setting_urls, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0048_add_article_details_to_review_assignment_email.py b/src/core/migrations/0048_add_article_details_to_review_assignment_email.py index 9bab098496..9ef32b5f99 100644 --- a/src/core/migrations/0048_add_article_details_to_review_assignment_email.py +++ b/src/core/migrations/0048_add_article_details_to_review_assignment_email.py @@ -8,17 +8,17 @@ from django.core.exceptions import FieldError -OLD_VALUE = "Dear {{ review_assignment.reviewer.full_name }},

We are requesting that you undertake a review of \"{{ article.title }}\" in {{ article.journal.name }}.

We woul d be most grateful for your time as the feedback from our reviewers is of the utmost importance to our editorial decision-making processes.

You can let us know your decision or decline to under take the review: {{ review_url }}

Regards,
{{ request.user.signature|safe }}" -NEW_VALUE = "Dear {{ review_assignment.reviewer.full_name }},

We are requesting that you undertake a review of \"{{ article.title }}\" in {{ article.journal.name }}.

We woul d be most grateful for your time as the feedback from our reviewers is of the utmost importance to our editorial decision-making processes.

You can let us know your decision or decline to under take the review: {{ review_url }}

{{ article_details }}

Regards,
{{ request.user.signature|safe }}" +OLD_VALUE = 'Dear {{ review_assignment.reviewer.full_name }},

We are requesting that you undertake a review of "{{ article.title }}" in {{ article.journal.name }}.

We woul d be most grateful for your time as the feedback from our reviewers is of the utmost importance to our editorial decision-making processes.

You can let us know your decision or decline to under take the review: {{ review_url }}

Regards,
{{ request.user.signature|safe }}' +NEW_VALUE = 'Dear {{ review_assignment.reviewer.full_name }},

We are requesting that you undertake a review of "{{ article.title }}" in {{ article.journal.name }}.

We woul d be most grateful for your time as the feedback from our reviewers is of the utmost importance to our editorial decision-making processes.

You can let us know your decision or decline to under take the review: {{ review_url }}

{{ article_details }}

Regards,
{{ request.user.signature|safe }}' def update_setting_values(apps, schema_editor): try: - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") setting_values = SettingValueTranslation.objects.filter( - master__setting__name='review_assignment', - master__setting__group__name='email', + master__setting__name="review_assignment", + master__setting__group__name="email", ) for setting in setting_values: @@ -27,14 +27,14 @@ def update_setting_values(apps, schema_editor): setting.value = NEW_VALUE elif "article_detail" not in setting.value: # otherwise, append the metadata at the end - setting.value += ("
{{ article_details }}") + setting.value += "
{{ article_details }}" setting.save() except (LookupError, FieldError): with translation.override(django_settings.LANGUAGE_CODE): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( - setting__name='review_assignment', - setting__group__name='email', + setting__name="review_assignment", + setting__group__name="email", ) for setting in setting_values: value_attr_name = "value_{}".format(django_settings.LANGUAGE_CODE) @@ -50,9 +50,8 @@ def update_setting_values(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0047_remove_dots_from_urls'), + ("core", "0047_remove_dots_from_urls"), ] operations = [ diff --git a/src/core/migrations/0048_modeltranslations_data.py b/src/core/migrations/0048_modeltranslations_data.py index 730722ea1b..721efc8b6b 100644 --- a/src/core/migrations/0048_modeltranslations_data.py +++ b/src/core/migrations/0048_modeltranslations_data.py @@ -6,21 +6,24 @@ def migrate_settings(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') + SettingValue = apps.get_model("core", "SettingValue") + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") translations = SettingValueTranslation.objects.all() for translation in translations: setting_value = SettingValue.objects.get(pk=translation.master_id) - setattr(setting_value, 'value_{}'.format(translation.language_code), translation.hvad_value) + setattr( + setting_value, + "value_{}".format(translation.language_code), + translation.hvad_value, + ) setting_value.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0047_hvad_to_modeltranslations'), + ("core", "0047_hvad_to_modeltranslations"), ] operations = [ diff --git a/src/core/migrations/0049_fix_review_accept_acknowledgement_url.py b/src/core/migrations/0049_fix_review_accept_acknowledgement_url.py index 5fb35478b2..9a08e32d7f 100644 --- a/src/core/migrations/0049_fix_review_accept_acknowledgement_url.py +++ b/src/core/migrations/0049_fix_review_accept_acknowledgement_url.py @@ -14,14 +14,16 @@ def replace_setting_urls(apps, schema_editor): try: - SettingValueTranslation = apps.get_model('core', 'SettingValueTranslation') - settings = SettingValueTranslation.objects.filter(master__setting__group__name="email") + SettingValueTranslation = apps.get_model("core", "SettingValueTranslation") + settings = SettingValueTranslation.objects.filter( + master__setting__group__name="email" + ) for s in settings: fix_url(s) except (LookupError, FieldError): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") settings = SettingValue.objects.filter( - setting__group__name='email', + setting__group__name="email", ) for s in settings: fix_url(s) @@ -36,11 +38,12 @@ def fix_url(setting): class Migration(migrations.Migration): - dependencies = [ - ('core', '0048_add_article_details_to_review_assignment_email'), + ("core", "0048_add_article_details_to_review_assignment_email"), ] operations = [ - migrations.RunPython(replace_setting_urls, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_setting_urls, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0049_modeltranslations_tidy.py b/src/core/migrations/0049_modeltranslations_tidy.py index d584adbbcf..7f27fc4de6 100644 --- a/src/core/migrations/0049_modeltranslations_tidy.py +++ b/src/core/migrations/0049_modeltranslations_tidy.py @@ -6,23 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0048_modeltranslations_data'), + ("core", "0048_modeltranslations_data"), ] operations = [ migrations.AlterUniqueTogether( - name='settingvaluetranslation', + name="settingvaluetranslation", unique_together=set([]), ), migrations.RemoveField( - model_name='settingvaluetranslation', - name='master', + model_name="settingvaluetranslation", + name="master", ), migrations.AlterModelManagers( - name='settingvalue', - managers=[ - ], + name="settingvalue", + managers=[], ), ] diff --git a/src/core/migrations/0050_auto_20210513_0943.py b/src/core/migrations/0050_auto_20210513_0943.py index a01a4a3db7..c089c0d386 100644 --- a/src/core/migrations/0050_auto_20210513_0943.py +++ b/src/core/migrations/0050_auto_20210513_0943.py @@ -6,50 +6,49 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0049_modeltranslations_tidy'), + ("core", "0049_modeltranslations_tidy"), ] operations = [ migrations.AddField( - model_name='editorialgroup', - name='description_cy', + model_name="editorialgroup", + name="description_cy", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='description_de', + model_name="editorialgroup", + name="description_de", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='description_en', + model_name="editorialgroup", + name="description_en", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='description_fr', + model_name="editorialgroup", + name="description_fr", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_cy', + model_name="editorialgroup", + name="name_cy", field=models.CharField(max_length=500, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_de', + model_name="editorialgroup", + name="name_de", field=models.CharField(max_length=500, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_en', + model_name="editorialgroup", + name="name_en", field=models.CharField(max_length=500, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_fr', + model_name="editorialgroup", + name="name_fr", field=models.CharField(max_length=500, null=True), ), ] diff --git a/src/core/migrations/0050_xslt_1-3-10.py b/src/core/migrations/0050_xslt_1-3-10.py index c142e82958..681f64ad12 100644 --- a/src/core/migrations/0050_xslt_1-3-10.py +++ b/src/core/migrations/0050_xslt_1-3-10.py @@ -19,7 +19,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -40,7 +40,7 @@ def upgrade(apps, schema_editor): except XSLFile.DoesNotExist: old_default = None xsl_path = os.path.join(settings.BASE_DIR, "transform/xsl/default.xsl") - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = "default.xsl" @@ -60,12 +60,9 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0049_fix_review_accept_acknowledgement_url'), - ('journal', '0041_issue_short_description'), + ("core", "0049_fix_review_accept_acknowledgement_url"), + ("journal", "0041_issue_short_description"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0051_auto_20210513_1206.py b/src/core/migrations/0051_auto_20210513_1206.py index f71ce31ac3..2eec7bf42a 100644 --- a/src/core/migrations/0051_auto_20210513_1206.py +++ b/src/core/migrations/0051_auto_20210513_1206.py @@ -6,50 +6,49 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0050_auto_20210513_0943'), + ("core", "0050_auto_20210513_0943"), ] operations = [ migrations.AddField( - model_name='contacts', - name='name_cy', + model_name="contacts", + name="name_cy", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='name_de', + model_name="contacts", + name="name_de", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='name_en', + model_name="contacts", + name="name_en", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='name_fr', + model_name="contacts", + name="name_fr", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='role_cy', + model_name="contacts", + name="role_cy", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='contacts', - name='role_de', + model_name="contacts", + name="role_de", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='contacts', - name='role_en', + model_name="contacts", + name="role_en", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='contacts', - name='role_fr', + model_name="contacts", + name="role_fr", field=models.CharField(max_length=200, null=True), ), ] diff --git a/src/core/migrations/0051_auto_20210607_1100.py b/src/core/migrations/0051_auto_20210607_1100.py index 91addc2c10..82d79d0a54 100644 --- a/src/core/migrations/0051_auto_20210607_1100.py +++ b/src/core/migrations/0051_auto_20210607_1100.py @@ -8,7 +8,7 @@ def lower_all_usernames(apps, schema_editor): - Account = apps.get_model('core', 'Account') + Account = apps.get_model("core", "Account") accounts = Account.objects.all() handled = set() @@ -25,8 +25,8 @@ def lower_all_usernames(apps, schema_editor): def handle_unique_username_violation(same_accounts, apps): - AccountRole = apps.get_model('core', 'Accountrole') - Account = apps.get_model('core', 'Account') + AccountRole = apps.get_model("core", "Accountrole") + Account = apps.get_model("core", "Account") real_account = None # Try checking if one has logged in @@ -36,11 +36,13 @@ def handle_unique_username_violation(same_accounts, apps): if real_account is None: # Try checking if one account has a non-author role: - account_roles_ids = AccountRole.objects.filter( - user__in=same_accounts, - ).exclude( - role__slug="author" - ).values_list("user", flat=True) + account_roles_ids = ( + AccountRole.objects.filter( + user__in=same_accounts, + ) + .exclude(role__slug="author") + .values_list("user", flat=True) + ) if len(set(account_roles_ids)) == 1: real_account = Account.objects.get(id=account_roles_ids[0]) @@ -52,7 +54,8 @@ def handle_unique_username_violation(same_accounts, apps): authors.append(acc) if len(authors) > 2: raise Exception( - "Can't workout the real user for username %s" % acc.username) + "Can't workout the real user for username %s" % acc.username + ) elif len(authors) == 1: real_account = authors[0] @@ -72,9 +75,8 @@ def handle_unique_username_violation(same_accounts, apps): class Migration(migrations.Migration): - dependencies = [ - ('core', '0050_xslt_1-3-10'), + ("core", "0050_xslt_1-3-10"), ] operations = [ diff --git a/src/core/migrations/0052_merge_20210624_0923.py b/src/core/migrations/0052_merge_20210624_0923.py index 2740ba34f5..5fe9d3101a 100644 --- a/src/core/migrations/0052_merge_20210624_0923.py +++ b/src/core/migrations/0052_merge_20210624_0923.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0051_auto_20210607_1100'), - ('core', '0051_auto_20210513_1206'), + ("core", "0051_auto_20210607_1100"), + ("core", "0051_auto_20210513_1206"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0053_auto_20210629_0641.py b/src/core/migrations/0053_auto_20210629_0641.py index c7e86e19a7..e121496088 100644 --- a/src/core/migrations/0053_auto_20210629_0641.py +++ b/src/core/migrations/0053_auto_20210629_0641.py @@ -6,35 +6,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0052_merge_20210624_0923'), + ("core", "0052_merge_20210624_0923"), ] operations = [ migrations.AddField( - model_name='contacts', - name='name_nl', + model_name="contacts", + name="name_nl", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='role_nl', + model_name="contacts", + name="role_nl", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='description_nl', + model_name="editorialgroup", + name="description_nl", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_nl', + model_name="editorialgroup", + name="name_nl", field=models.CharField(max_length=500, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_nl', + model_name="settingvalue", + name="value_nl", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/core/migrations/0054_auto_20210729_0919.py b/src/core/migrations/0054_auto_20210729_0919.py index 1b65de139c..1300a1197e 100644 --- a/src/core/migrations/0054_auto_20210729_0919.py +++ b/src/core/migrations/0054_auto_20210729_0919.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0053_auto_20210629_0641'), + ("core", "0053_auto_20210629_0641"), ] operations = [ migrations.AlterModelOptions( - name='settingvaluetranslation', - options={'managed': False}, + name="settingvaluetranslation", + options={"managed": False}, ), ] diff --git a/src/core/migrations/0055_extend_file_label_field.py b/src/core/migrations/0055_extend_file_label_field.py index 50ba68ee98..4c97cb7967 100644 --- a/src/core/migrations/0055_extend_file_label_field.py +++ b/src/core/migrations/0055_extend_file_label_field.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0054_auto_20210729_0919'), + ("core", "0054_auto_20210729_0919"), ] operations = [ migrations.AlterField( - model_name='file', - name='label', - field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Label'), + model_name="file", + name="label", + field=models.CharField( + blank=True, max_length=1000, null=True, verbose_name="Label" + ), ), ] diff --git a/src/core/migrations/0056_auto_20210913_0818.py b/src/core/migrations/0056_auto_20210913_0818.py index 46d7dbc153..e77584ad48 100644 --- a/src/core/migrations/0056_auto_20210913_0818.py +++ b/src/core/migrations/0056_auto_20210913_0818.py @@ -10,29 +10,26 @@ def remove_popular_article(apps, schema_editor): Removes a popular_articles in case a migration was run before the reversion of this plugin name. """ - SettingGroup = apps.get_model('core', 'SettingGroup') - Setting = apps.get_model('core', 'Setting') - SettingValue = apps.get_model('core', 'SettingValue') - Plugin = apps.get_model('utils', 'Plugin') + SettingGroup = apps.get_model("core", "SettingGroup") + Setting = apps.get_model("core", "Setting") + SettingValue = apps.get_model("core", "SettingValue") + Plugin = apps.get_model("utils", "Plugin") SettingValue.objects.filter( - setting__group__name='plugin:popular_articles', + setting__group__name="plugin:popular_articles", ).delete() Setting.objects.filter( - group__name='plugin:popular_articles', + group__name="plugin:popular_articles", ).delete() SettingGroup.objects.filter( - name='plugin:popular_articles', - ).delete() - Plugin.objects.filter( - name='popular_articles' + name="plugin:popular_articles", ).delete() + Plugin.objects.filter(name="popular_articles").delete() class Migration(migrations.Migration): - dependencies = [ - ('core', '0055_extend_file_label_field'), + ("core", "0055_extend_file_label_field"), ] operations = [ diff --git a/src/core/migrations/0057_auto_20211004_1130.py b/src/core/migrations/0057_auto_20211004_1130.py index 684c8b9f2d..3a5bc0ac2d 100644 --- a/src/core/migrations/0057_auto_20211004_1130.py +++ b/src/core/migrations/0057_auto_20211004_1130.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0056_auto_20210913_0818'), + ("core", "0056_auto_20210913_0818"), ] operations = [ migrations.AddField( - model_name='galley', - name='public', + model_name="galley", + name="public", field=models.BooleanField(default=True), ), ] diff --git a/src/core/migrations/0057_xslt_1-4-0.py b/src/core/migrations/0057_xslt_1-4-0.py index c1230f70f6..6f57d85d4c 100644 --- a/src/core/migrations/0057_xslt_1-4-0.py +++ b/src/core/migrations/0057_xslt_1-4-0.py @@ -17,7 +17,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -36,7 +36,7 @@ def upgrade(apps, schema_editor): old_default = None xsl_path = os.path.join(settings.BASE_DIR, "transform/xsl/default.xsl") if not XSLFile.objects.filter(label=LATEST_LABEL).exists(): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = "default-v1.4.xsl" @@ -54,11 +54,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0056_auto_20210913_0818'), + ("core", "0056_auto_20210913_0818"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0058_merge_20211005_0909.py b/src/core/migrations/0058_merge_20211005_0909.py index 52205ce5bb..1825809e55 100644 --- a/src/core/migrations/0058_merge_20211005_0909.py +++ b/src/core/migrations/0058_merge_20211005_0909.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0057_xslt_1-4-0'), - ('core', '0057_auto_20211004_1130'), + ("core", "0057_xslt_1-4-0"), + ("core", "0057_auto_20211004_1130"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0059_auto_20211013_1657.py b/src/core/migrations/0059_auto_20211013_1657.py index 7fc8fc9593..06ce71c9d1 100644 --- a/src/core/migrations/0059_auto_20211013_1657.py +++ b/src/core/migrations/0059_auto_20211013_1657.py @@ -6,21 +6,23 @@ def set_about_plugin_to_hpe(apps, schema_editor): - Plugin = apps.get_model('utils', 'Plugin') + Plugin = apps.get_model("utils", "Plugin") Plugin.objects.filter( - name='About', + name="About", ).update( homepage_element=True, ) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0058_merge_20211005_0909'), - ('utils', '0023_upgrade_1_4_0'), + ("core", "0058_merge_20211005_0909"), + ("utils", "0023_upgrade_1_4_0"), ] operations = [ - migrations.RunPython(set_about_plugin_to_hpe, reverse_code=migrations.RunPython.noop) + migrations.RunPython( + set_about_plugin_to_hpe, reverse_code=migrations.RunPython.noop + ) ] diff --git a/src/core/migrations/0060_remove_display_about_on_submissions.py b/src/core/migrations/0060_remove_display_about_on_submissions.py index 0fc3ed9e9c..bf347f7d72 100644 --- a/src/core/migrations/0060_remove_display_about_on_submissions.py +++ b/src/core/migrations/0060_remove_display_about_on_submissions.py @@ -8,30 +8,28 @@ def delete_display_about_on_submissions(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - group__name='general', - name='display_about_on_submissions', + group__name="general", + name="display_about_on_submissions", ).delete() def add_display_about_on_submissions(apps, schema_editor): setting_handler.create_setting( - setting_group_name='general', - setting_name='display_about_on_submissions', - type='boolean', + setting_group_name="general", + setting_name="display_about_on_submissions", + type="boolean", pretty_name="Display 'About' on submissions", - description='If enabled, the journal about block will be rendered in the submissions page', + description="If enabled, the journal about block will be rendered in the submissions page", is_translatable=False, - default_value=' ', + default_value=" ", ) - class Migration(migrations.Migration): - dependencies = [ - ('core', '0059_auto_20211013_1657'), + ("core", "0059_auto_20211013_1657"), ] operations = [ diff --git a/src/core/migrations/0061_auto_20220107_1208.py b/src/core/migrations/0061_auto_20220107_1208.py index a288be8333..4e0f97b109 100644 --- a/src/core/migrations/0061_auto_20220107_1208.py +++ b/src/core/migrations/0061_auto_20220107_1208.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0060_remove_display_about_on_submissions'), + ("core", "0060_remove_display_about_on_submissions"), ] operations = [ migrations.AlterField( - model_name='domainalias', - name='domain', + model_name="domainalias", + name="domain", field=models.CharField(blank=True, max_length=255, null=True, unique=True), ), ] diff --git a/src/core/migrations/0061_auto_20220109_1800.py b/src/core/migrations/0061_auto_20220109_1800.py index 50ff2970f2..000e4b2b89 100644 --- a/src/core/migrations/0061_auto_20220109_1800.py +++ b/src/core/migrations/0061_auto_20220109_1800.py @@ -15,7 +15,8 @@ def update_settings_group(apps, setting_names, from_group, to_group): SettingGroup = apps.get_model("core", "SettingGroup") setting_group, _ = SettingGroup.objects.get_or_create(name=to_group) Setting.objects.filter( - name__in=STYLING_SETTINGS, group__name=from_group, + name__in=STYLING_SETTINGS, + group__name=from_group, ).update(group=setting_group) @@ -28,9 +29,8 @@ def styling_to_general(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0060_remove_display_about_on_submissions'), + ("core", "0060_remove_display_about_on_submissions"), ] operations = [ diff --git a/src/core/migrations/0061_auto_20220208_1654.py b/src/core/migrations/0061_auto_20220208_1654.py index 158d7be5ce..a1e7304002 100644 --- a/src/core/migrations/0061_auto_20220208_1654.py +++ b/src/core/migrations/0061_auto_20220208_1654.py @@ -9,29 +9,59 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0026_merge_20211011_0906'), - ('journal', '0046_auto_20210922_1436'), - ('core', '0060_remove_display_about_on_submissions'), + ("repository", "0026_merge_20211011_0906"), + ("journal", "0046_auto_20210922_1436"), + ("core", "0060_remove_display_about_on_submissions"), ] operations = [ migrations.CreateModel( - name='AccessRequest', + name="AccessRequest", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('requested', models.DateTimeField(default=django.utils.timezone.now)), - ('processed', models.BooleanField(default=False)), - ('text', models.TextField(blank=True, null=True)), - ('journal', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), - ('repository', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.Repository')), - ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Role')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("requested", models.DateTimeField(default=django.utils.timezone.now)), + ("processed", models.BooleanField(default=False)), + ("text", models.TextField(blank=True, null=True)), + ( + "journal", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), + ( + "repository", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.Repository", + ), + ), + ( + "role", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.Role" + ), + ), ], ), migrations.AddField( - model_name='accessrequest', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="accessrequest", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), ), ] diff --git a/src/core/migrations/0062_auto_20220209_1556.py b/src/core/migrations/0062_auto_20220209_1556.py index 433118f06d..7a4c4adbb3 100644 --- a/src/core/migrations/0062_auto_20220209_1556.py +++ b/src/core/migrations/0062_auto_20220209_1556.py @@ -6,30 +6,650 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0061_auto_20220208_1654'), + ("core", "0061_auto_20220208_1654"), ] operations = [ migrations.AddField( - model_name='accessrequest', - name='evaluation_note', - field=models.TextField(help_text='This note will be sent to the requester when you approve or decline their request.', null=True), + model_name="accessrequest", + name="evaluation_note", + field=models.TextField( + help_text="This note will be sent to the requester when you approve or decline their request.", + null=True, + ), ), migrations.AlterField( - model_name='account', - name='preferred_timezone', - field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], max_length=300, null=True), + model_name="account", + name="preferred_timezone", + field=models.CharField( + blank=True, + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kanton", "Pacific/Kanton"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + max_length=300, + null=True, + ), ), migrations.AlterField( - model_name='galley', - name='label', - field=models.CharField(help_text='Typeset file labels are displayed in download links and have the format "Download Label" eg. if you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for you, leave it blank.', max_length=400), + model_name="galley", + name="label", + field=models.CharField( + help_text='Typeset file labels are displayed in download links and have the format "Download Label" eg. if you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for you, leave it blank.', + max_length=400, + ), ), migrations.AlterField( - model_name='galley', - name='public', - field=models.BooleanField(default=True, help_text='Uncheck if the typeset file should not be publicly available after the article is published.'), + model_name="galley", + name="public", + field=models.BooleanField( + default=True, + help_text="Uncheck if the typeset file should not be publicly available after the article is published.", + ), ), ] diff --git a/src/core/migrations/0062_merge_20220110_1934.py b/src/core/migrations/0062_merge_20220110_1934.py index 5f824fcd7e..e987df927b 100644 --- a/src/core/migrations/0062_merge_20220110_1934.py +++ b/src/core/migrations/0062_merge_20220110_1934.py @@ -6,26 +6,642 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0061_auto_20220107_1208'), - ('core', '0061_auto_20220109_1800'), + ("core", "0061_auto_20220107_1208"), + ("core", "0061_auto_20220109_1800"), ] operations = [ migrations.AlterField( - model_name='account', - name='preferred_timezone', - field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], max_length=300, null=True), + model_name="account", + name="preferred_timezone", + field=models.CharField( + blank=True, + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + max_length=300, + null=True, + ), ), migrations.AlterField( - model_name='galley', - name='label', - field=models.CharField(help_text='Typeset file labels are displayed in download links and have the format "Download Label" eg. if you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for you, leave it blank.', max_length=400), + model_name="galley", + name="label", + field=models.CharField( + help_text='Typeset file labels are displayed in download links and have the format "Download Label" eg. if you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for you, leave it blank.', + max_length=400, + ), ), migrations.AlterField( - model_name='galley', - name='public', - field=models.BooleanField(default=True, help_text='Uncheck if the typeset file should not be publicly available after the article is published.'), + model_name="galley", + name="public", + field=models.BooleanField( + default=True, + help_text="Uncheck if the typeset file should not be publicly available after the article is published.", + ), ), ] diff --git a/src/core/migrations/0063_fix_review_accept_ack.py b/src/core/migrations/0063_fix_review_accept_ack.py index dfe7118905..e66e62e849 100644 --- a/src/core/migrations/0063_fix_review_accept_ack.py +++ b/src/core/migrations/0063_fix_review_accept_ack.py @@ -8,15 +8,15 @@ def update_review_accept_acknowledgement(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( - setting__group__name='email', - setting__name='review_accept_acknowledgement', + setting__group__name="email", + setting__name="review_accept_acknowledgement", ) # Loop over all SettingValues and update all of the value strings for each available language. for setting_value in setting_values: for language in settings.LANGUAGES: - value_string = 'value_{}'.format(language[0]) + value_string = "value_{}".format(language[0]) if setting_value and hasattr(setting_value, value_string): old_string = getattr( @@ -24,10 +24,15 @@ def update_review_accept_acknowledgement(apps, schema_editor): value_string, ) if old_string: - for replacement in ( - ('{{ request.user.signature }}', '{{ review_assignment.editor.signature|safe }}'), - ('{{ request.user.signature|safe }}', '{{ review_assignment.editor.signature|safe }}') + ( + "{{ request.user.signature }}", + "{{ review_assignment.editor.signature|safe }}", + ), + ( + "{{ request.user.signature|safe }}", + "{{ review_assignment.editor.signature|safe }}", + ), ): old_string = old_string.replace(*replacement) @@ -40,9 +45,8 @@ def update_review_accept_acknowledgement(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0062_merge_20220110_1934'), + ("core", "0062_merge_20220110_1934"), ] operations = [ diff --git a/src/core/migrations/0064_auto_20220201_1038.py b/src/core/migrations/0064_auto_20220201_1038.py index 50c5471593..64f8828c79 100644 --- a/src/core/migrations/0064_auto_20220201_1038.py +++ b/src/core/migrations/0064_auto_20220201_1038.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0063_fix_review_accept_ack'), + ("core", "0063_fix_review_accept_ack"), ] operations = [ migrations.AlterField( - model_name='account', - name='institution', - field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Institution'), + model_name="account", + name="institution", + field=models.CharField( + blank=True, max_length=1000, null=True, verbose_name="Institution" + ), ), ] diff --git a/src/core/migrations/0064_auto_20220202_1426.py b/src/core/migrations/0064_auto_20220202_1426.py index b894c3110a..689df7c819 100644 --- a/src/core/migrations/0064_auto_20220202_1426.py +++ b/src/core/migrations/0064_auto_20220202_1426.py @@ -7,15 +7,15 @@ def update_password_reset(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( - setting__group__name='email', - setting__name='password_reset', + setting__group__name="email", + setting__name="password_reset", ) # Loop over all SettingValues and update all of the value strings for each available language. for setting_value in setting_values: for language in settings.LANGUAGES: - value_string = 'value_{}'.format(language[0]) + value_string = "value_{}".format(language[0]) if setting_value and hasattr(setting_value, value_string): old_string = getattr( @@ -23,9 +23,11 @@ def update_password_reset(apps, schema_editor): value_string, ) if old_string: - for replacement in ( - ('{{ core_reset_password_url }} br/>
', '{{ core_reset_password_url }}'), + ( + "{{ core_reset_password_url }} br/>
", + "{{ core_reset_password_url }}", + ), ): old_string = old_string.replace(*replacement) @@ -38,9 +40,8 @@ def update_password_reset(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0063_fix_review_accept_ack'), + ("core", "0063_fix_review_accept_ack"), ] operations = [ diff --git a/src/core/migrations/0064_auto_20220216_1213.py b/src/core/migrations/0064_auto_20220216_1213.py index 4d8bb75b66..ab0d47b134 100644 --- a/src/core/migrations/0064_auto_20220216_1213.py +++ b/src/core/migrations/0064_auto_20220216_1213.py @@ -7,27 +7,26 @@ def update_crossref_date_suffix(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( - setting__group__name='crossref', - setting__name='crossref_date_suffix', + setting__group__name="crossref", + setting__name="crossref_date_suffix", ) # Loop over all SettingValues and update all of the value strings for each available language. for setting_value in setting_values: for language in settings.LANGUAGES: - value_string = 'value_{}'.format(language[0]) + value_string = "value_{}".format(language[0]) setattr( setting_value, value_string, - '656', + "656", ) setting_value.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0063_fix_review_accept_ack'), + ("core", "0063_fix_review_accept_ack"), ] operations = [ diff --git a/src/core/migrations/0065_merge_20220218_1347.py b/src/core/migrations/0065_merge_20220218_1347.py index f8cde41c98..32cdb7f02a 100644 --- a/src/core/migrations/0065_merge_20220218_1347.py +++ b/src/core/migrations/0065_merge_20220218_1347.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0064_auto_20220202_1426'), - ('core', '0064_auto_20220216_1213'), + ("core", "0064_auto_20220202_1426"), + ("core", "0064_auto_20220216_1213"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0065_merge_20220223_1229.py b/src/core/migrations/0065_merge_20220223_1229.py index 34fb6089ef..9c93cc3e56 100644 --- a/src/core/migrations/0065_merge_20220223_1229.py +++ b/src/core/migrations/0065_merge_20220223_1229.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0062_auto_20220209_1556'), - ('core', '0064_auto_20220202_1426'), + ("core", "0062_auto_20220209_1556"), + ("core", "0064_auto_20220202_1426"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0066_merge_20220315_1535.py b/src/core/migrations/0066_merge_20220315_1535.py index 5dd7a1906a..3676b4099d 100644 --- a/src/core/migrations/0066_merge_20220315_1535.py +++ b/src/core/migrations/0066_merge_20220315_1535.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0065_merge_20220223_1229'), - ('core', '0065_merge_20220218_1347'), + ("core", "0065_merge_20220223_1229"), + ("core", "0065_merge_20220218_1347"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0067_galley_last_modified.py b/src/core/migrations/0067_galley_last_modified.py index b30bf433fb..d1b9059d27 100644 --- a/src/core/migrations/0067_galley_last_modified.py +++ b/src/core/migrations/0067_galley_last_modified.py @@ -6,20 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0066_merge_20220315_1535'), + ("core", "0066_merge_20220315_1535"), ] operations = [ migrations.AddField( - model_name='galley', - name='last_modified', + model_name="galley", + name="last_modified", field=models.DateTimeField(auto_now=True), ), migrations.RenameField( - model_name='file', - old_name='date_modified', - new_name='last_modified', + model_name="file", + old_name="date_modified", + new_name="last_modified", ), ] diff --git a/src/core/migrations/0068_xslt_1-4-1.py b/src/core/migrations/0068_xslt_1-4-1.py index 213a0c14c6..f35c9fb93a 100644 --- a/src/core/migrations/0068_xslt_1-4-1.py +++ b/src/core/migrations/0068_xslt_1-4-1.py @@ -18,7 +18,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -39,7 +39,7 @@ def upgrade(apps, schema_editor): not XSLFile.objects.filter(label=LATEST_LABEL).exists() and LATEST_LABEL == settings.DEFAULT_XSL_FILE_LABEL ): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = "default-v1.4.1.xsl" @@ -57,11 +57,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0067_galley_last_modified'), + ("core", "0067_galley_last_modified"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0069_merge_20220427_1042.py b/src/core/migrations/0069_merge_20220427_1042.py index bfedb592fc..9ea8906c6d 100644 --- a/src/core/migrations/0069_merge_20220427_1042.py +++ b/src/core/migrations/0069_merge_20220427_1042.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0068_xslt_1-4-1'), - ('core', '0064_auto_20220201_1038'), + ("core", "0068_xslt_1-4-1"), + ("core", "0064_auto_20220201_1038"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0070_auto_20220506_1652.py b/src/core/migrations/0070_auto_20220506_1652.py index e0eb8f8db1..14ad260bc7 100644 --- a/src/core/migrations/0070_auto_20220506_1652.py +++ b/src/core/migrations/0070_auto_20220506_1652.py @@ -9,58 +9,81 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0069_merge_20220427_1042'), + ("core", "0069_merge_20220427_1042"), ] operations = [ migrations.CreateModel( - name='PGFileText', + name="PGFileText", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('contents', django.contrib.postgres.search.SearchVectorField(blank=True, editable=False, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "contents", + django.contrib.postgres.search.SearchVectorField( + blank=True, editable=False, null=True + ), + ), ], options={ - 'abstract': False, - 'required_db_vendor': 'postgresql', + "abstract": False, + "required_db_vendor": "postgresql", }, ), migrations.CreateModel( - name='FileText', + name="FileText", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], options={ - 'swappable': 'CORE_FILETEXT_MODEL', + "swappable": "CORE_FILETEXT_MODEL", }, ), migrations.AddField( - model_name='file', - name='text', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=settings.CORE_FILETEXT_MODEL, - related_name='file', + model_name="file", + name="text", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.CORE_FILETEXT_MODEL, + related_name="file", ), ), migrations.AlterField( - model_name='file', - name='history', - field=models.ManyToManyField(blank=True, to='core.FileHistory'), + model_name="file", + name="history", + field=models.ManyToManyField(blank=True, to="core.FileHistory"), ), migrations.AddField( - model_name='filetext', - name='contents', + model_name="filetext", + name="contents", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='filetext', - name='date_populated', + model_name="filetext", + name="date_populated", field=models.DateTimeField(default=django.utils.timezone.now), ), migrations.AddField( - model_name='pgfiletext', - name='date_populated', + model_name="pgfiletext", + name="date_populated", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/src/core/migrations/0071_xslt_1-4-2.py b/src/core/migrations/0071_xslt_1-4-2.py index e81dfe5d39..407423ff21 100644 --- a/src/core/migrations/0071_xslt_1-4-2.py +++ b/src/core/migrations/0071_xslt_1-4-2.py @@ -20,7 +20,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -41,7 +41,7 @@ def upgrade(apps, schema_editor): not XSLFile.objects.filter(label=LATEST_LABEL).exists() and LATEST_LABEL == settings.DEFAULT_XSL_FILE_LABEL ): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = FILE_NAME @@ -59,11 +59,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0070_auto_20220506_1652'), + ("core", "0070_auto_20220506_1652"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0072_auto_20220623_1028.py b/src/core/migrations/0072_auto_20220623_1028.py index c104cc280e..42b5acb375 100644 --- a/src/core/migrations/0072_auto_20220623_1028.py +++ b/src/core/migrations/0072_auto_20220623_1028.py @@ -8,15 +8,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0071_xslt_1-4-2'), + ("core", "0071_xslt_1-4-2"), ] operations = [ migrations.AlterField( - model_name='file', - name='text', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='file', to=settings.CORE_FILETEXT_MODEL), + model_name="file", + name="text", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="file", + to=settings.CORE_FILETEXT_MODEL, + ), ), ] diff --git a/src/core/migrations/0073_auto_20220630_1608.py b/src/core/migrations/0073_auto_20220630_1608.py index 66f1844b08..5714778a62 100644 --- a/src/core/migrations/0073_auto_20220630_1608.py +++ b/src/core/migrations/0073_auto_20220630_1608.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0072_auto_20220623_1028'), + ("core", "0072_auto_20220623_1028"), ] operations = [ migrations.AlterField( - model_name='account', - name='username', - field=models.CharField(max_length=254, unique=True, verbose_name='Username'), + model_name="account", + name="username", + field=models.CharField( + max_length=254, unique=True, verbose_name="Username" + ), ), ] diff --git a/src/core/migrations/0074_account_suffix.py b/src/core/migrations/0074_account_suffix.py index 90bad40e3a..4d53ee7765 100644 --- a/src/core/migrations/0074_account_suffix.py +++ b/src/core/migrations/0074_account_suffix.py @@ -6,18 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0074_fix_editorial_images_group'), + ("core", "0074_fix_editorial_images_group"), ] operations = [ migrations.AddField( - model_name='account', - name='suffix', + model_name="account", + name="suffix", field=models.CharField( blank=True, - verbose_name='Name suffix', + verbose_name="Name suffix", max_length=300, null=True, ), diff --git a/src/core/migrations/0074_auto_20220721_1448.py b/src/core/migrations/0074_auto_20220721_1448.py index 5401146ea8..71610ad513 100644 --- a/src/core/migrations/0074_auto_20220721_1448.py +++ b/src/core/migrations/0074_auto_20220721_1448.py @@ -14,22 +14,22 @@ def update_default_setting(apps, schema_editor): - migration_utils.update_default_setting_values( apps, - setting_name='review_assignment', - group_name='email', + setting_name="review_assignment", + group_name="email", values_to_replace=[OLD_DEFAULT_VALUE, VARIANT_ONE, VARIANT_TWO], replacement_value=NEW_VALUE, ) class Migration(migrations.Migration): - dependencies = [ - ('core', '0074_account_suffix'), + ("core", "0074_account_suffix"), ] operations = [ - migrations.RunPython(update_default_setting, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + update_default_setting, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0074_fix_editorial_images_group.py b/src/core/migrations/0074_fix_editorial_images_group.py index c6474d2a08..357e09e1b8 100644 --- a/src/core/migrations/0074_fix_editorial_images_group.py +++ b/src/core/migrations/0074_fix_editorial_images_group.py @@ -7,7 +7,7 @@ possible wrong states. Case A: pre-existing installation ran migration core.0061: - - In this state the settings where swapped correctly to styling. however, + - In this state the settings where swapped correctly to styling. however, due to the setting group not being updated, a duplicate setting with the group general was re-introduced. This setting will not have any relevant values attached, since the codebase has been using the correct setting @@ -23,6 +23,7 @@ (essentially run 0061 again) """ + from __future__ import unicode_literals from django.db import migrations, models @@ -60,12 +61,12 @@ def general_to_styling(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0073_auto_20220630_1608'), + ("core", "0073_auto_20220630_1608"), ] operations = [ migrations.RunPython( - general_to_styling, reverse_code=migrations.RunPython.noop), + general_to_styling, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0075_reader_role.py b/src/core/migrations/0075_reader_role.py index 20d8a83ed4..c2590edb34 100644 --- a/src/core/migrations/0075_reader_role.py +++ b/src/core/migrations/0075_reader_role.py @@ -6,22 +6,21 @@ class Migration(migrations.Migration): - def forwards_func(apps, schema_editor): Role = apps.get_model("core", "Role") Role.objects.get_or_create( - name='Reader', - slug='reader', + name="Reader", + slug="reader", ) def reverse_func(apps, schema_editor): Role = apps.get_model("core", "Role") Role.objects.filter( - slug='reader', + slug="reader", ).delete() dependencies = [ - ('core', '0074_auto_20220721_1448'), + ("core", "0074_auto_20220721_1448"), ] operations = [ diff --git a/src/core/migrations/0075_xslt_1-4-3.py b/src/core/migrations/0075_xslt_1-4-3.py index 81591c1ae4..4cc057edc8 100644 --- a/src/core/migrations/0075_xslt_1-4-3.py +++ b/src/core/migrations/0075_xslt_1-4-3.py @@ -21,7 +21,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -42,7 +42,7 @@ def upgrade(apps, schema_editor): not XSLFile.objects.filter(label=LATEST_LABEL).exists() and LATEST_LABEL == settings.DEFAULT_XSL_FILE_LABEL ): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = FILE_NAME @@ -60,11 +60,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0074_auto_20220721_1448'), + ("core", "0074_auto_20220721_1448"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0076_remove_old_setting.py b/src/core/migrations/0076_remove_old_setting.py index 2d03ab7412..0abce03f72 100644 --- a/src/core/migrations/0076_remove_old_setting.py +++ b/src/core/migrations/0076_remove_old_setting.py @@ -6,22 +6,20 @@ def remove_old_setting(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - group__name='general', - name='hide_review_metadata_from_authors', + group__name="general", + name="hide_review_metadata_from_authors", ).delete() class Migration(migrations.Migration): - dependencies = [ - ('core', '0075_reader_role'), + ("core", "0075_reader_role"), ] operations = [ migrations.RunPython( - remove_old_setting, - reverse_code=migrations.RunPython.noop + remove_old_setting, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/core/migrations/0077_merge_20221004_1031.py b/src/core/migrations/0077_merge_20221004_1031.py index e9dee86ddb..6d89723b05 100644 --- a/src/core/migrations/0077_merge_20221004_1031.py +++ b/src/core/migrations/0077_merge_20221004_1031.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0075_xslt_1-4-3'), - ('core', '0076_remove_old_setting'), + ("core", "0075_xslt_1-4-3"), + ("core", "0076_remove_old_setting"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0078_add_journal_manager_role.py b/src/core/migrations/0078_add_journal_manager_role.py index d4775998a4..44a2894860 100644 --- a/src/core/migrations/0078_add_journal_manager_role.py +++ b/src/core/migrations/0078_add_journal_manager_role.py @@ -8,19 +8,18 @@ class Migration(migrations.Migration): - def add_role(apps, schema_editor): Role = apps.get_model("core", "Role") role = Role( - name='Journal Manager', - slug='journal-manager', + name="Journal Manager", + slug="journal-manager", ) role.save() def remove_role(apps, schema_editor): Role = apps.get_model("core", "Role") Role.objects.filter( - slug='journal-manager', + slug="journal-manager", ).delete() def install_permissions(apps, schema_editor): @@ -33,20 +32,22 @@ def install_permissions(apps, schema_editor): pass dependencies = [ - ('core', '0077_merge_20221004_1031'), + ("core", "0077_merge_20221004_1031"), ] operations = [ migrations.RunPython(add_role, reverse_code=remove_role), migrations.AddField( - model_name='setting', - name='editable_by', - field=models.ManyToManyField(blank=True, - help_text='Determines who can edit this setting based on their assigned roles.', - to='core.Role'), + model_name="setting", + name="editable_by", + field=models.ManyToManyField( + blank=True, + help_text="Determines who can edit this setting based on their assigned roles.", + to="core.Role", + ), ), migrations.RunPython( install_permissions, reverse_code=migrations.RunPython.noop, ), - ] \ No newline at end of file + ] diff --git a/src/core/migrations/0078_anonymous_review_rewording.py b/src/core/migrations/0078_anonymous_review_rewording.py index 3b72998ed5..2011340742 100644 --- a/src/core/migrations/0078_anonymous_review_rewording.py +++ b/src/core/migrations/0078_anonymous_review_rewording.py @@ -4,7 +4,7 @@ SETTING_VALUES_TO_FIX = { "review_file_help": { "old": "

If you are undertaking a blind or double-blind review you must ensure that the file has any identifying text removed. Consider:

  1. If the paper has a title page removing the authors name and contact information.
  2. Check the bibliography and the text for self-citation
  3. Also check for mentions not made explicitly. The following phrases might give away an author’s identity and should be edited:
    • ‘As I previously discussed….’
    • ‘As I have showed…’
    • ‘My previous work….’
  4. Remove any identifying metadata from the file.
", - "new": "

If you are undertaking a single anonymous or double anonymous review you must ensure that the file has any identifying text removed. Consider:

  1. If the paper has a title page removing the authors name and contact information.
  2. Check the bibliography and the text for self-citation
  3. Also check for mentions not made explicitly. The following phrases might give away an author’s identity and should be edited:
    • ‘As I previously discussed….’
    • ‘As I have showed…’
    • ‘My previous work….’
  4. Remove any identifying metadata from the file.
" + "new": "

If you are undertaking a single anonymous or double anonymous review you must ensure that the file has any identifying text removed. Consider:

  1. If the paper has a title page removing the authors name and contact information.
  2. Check the bibliography and the text for self-citation
  3. Also check for mentions not made explicitly. The following phrases might give away an author’s identity and should be edited:
    • ‘As I previously discussed….’
    • ‘As I have showed…’
    • ‘My previous work….’
  4. Remove any identifying metadata from the file.
", }, "peer_review_info": { "old": "This journal operates a open/blind/double blind peer review policy.", @@ -22,12 +22,11 @@ def replace_settings(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') - SettingValue = apps.get_model('core', 'SettingValue') + Setting = apps.get_model("core", "Setting") + SettingValue = apps.get_model("core", "SettingValue") for setting_name, values in SETTING_VALUES_TO_FIX.items(): settings = SettingValue.objects.filter( - setting__name=setting_name, - value_en=values["old"] + setting__name=setting_name, value_en=values["old"] ) settings.update(value_en=values["new"]) @@ -43,9 +42,8 @@ def replace_settings(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0077_merge_20221004_1031'), + ("core", "0077_merge_20221004_1031"), ] operations = [ diff --git a/src/core/migrations/0078_auto_20230120_1546.py b/src/core/migrations/0078_auto_20230120_1546.py index a9dae61207..0410de55f3 100644 --- a/src/core/migrations/0078_auto_20230120_1546.py +++ b/src/core/migrations/0078_auto_20230120_1546.py @@ -6,25 +6,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0077_merge_20221004_1031'), + ("core", "0077_merge_20221004_1031"), ] operations = [ migrations.AlterField( - model_name='account', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.country', verbose_name='Country'), + model_name="account", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.country", + verbose_name="Country", + ), ), migrations.AlterField( - model_name='galley', - name='file', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.file'), + model_name="galley", + name="file", + field=models.ForeignKey( + null=True, on_delete=django.db.models.deletion.SET_NULL, to="core.file" + ), ), migrations.AlterField( - model_name='task', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='completed_by', to=settings.AUTH_USER_MODEL), + model_name="task", + name="completed_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="completed_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/core/migrations/0079_auto_20230208_1233.py b/src/core/migrations/0079_auto_20230208_1233.py index 3447d0c044..00a4cd6092 100644 --- a/src/core/migrations/0079_auto_20230208_1233.py +++ b/src/core/migrations/0079_auto_20230208_1233.py @@ -6,35 +6,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_anonymous_review_rewording'), + ("core", "0078_anonymous_review_rewording"), ] operations = [ migrations.AddField( - model_name='contacts', - name='name_en_us', + model_name="contacts", + name="name_en_us", field=models.CharField(max_length=300, null=True), ), migrations.AddField( - model_name='contacts', - name='role_en_us', + model_name="contacts", + name="role_en_us", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='description_en_us', + model_name="editorialgroup", + name="description_en_us", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='editorialgroup', - name='name_en_us', + model_name="editorialgroup", + name="name_en_us", field=models.CharField(max_length=500, null=True), ), migrations.AddField( - model_name='settingvalue', - name='value_en_us', + model_name="settingvalue", + name="value_en_us", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/core/migrations/0080_merge_20230317_1530.py b/src/core/migrations/0080_merge_20230317_1530.py index bbbdb12df3..ae988afe37 100644 --- a/src/core/migrations/0080_merge_20230317_1530.py +++ b/src/core/migrations/0080_merge_20230317_1530.py @@ -4,12 +4,10 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_add_journal_manager_role'), - ('core', '0078_auto_20230120_1546'), - ('core', '0079_auto_20230208_1233'), + ("core", "0078_add_journal_manager_role"), + ("core", "0078_auto_20230120_1546"), + ("core", "0079_auto_20230208_1233"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0081_alter_account_preferred_timezone.py b/src/core/migrations/0081_alter_account_preferred_timezone.py index 54ab0b73e5..a1cd8b8143 100644 --- a/src/core/migrations/0081_alter_account_preferred_timezone.py +++ b/src/core/migrations/0081_alter_account_preferred_timezone.py @@ -4,15 +4,625 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0080_merge_20230317_1530'), + ("core", "0080_merge_20230317_1530"), ] operations = [ migrations.AlterField( - model_name='account', - name='preferred_timezone', - field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], max_length=300, null=True), + model_name="account", + name="preferred_timezone", + field=models.CharField( + blank=True, + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + max_length=300, + null=True, + ), ), ] diff --git a/src/core/migrations/0082_account_name_prefix_20231204_1231.py b/src/core/migrations/0082_account_name_prefix_20231204_1231.py index 79583ef2c5..35673c0086 100644 --- a/src/core/migrations/0082_account_name_prefix_20231204_1231.py +++ b/src/core/migrations/0082_account_name_prefix_20231204_1231.py @@ -4,15 +4,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), + ("core", "0081_alter_account_preferred_timezone"), ] operations = [ migrations.AddField( - model_name='account', - name='name_prefix', + model_name="account", + name="name_prefix", field=models.CharField(blank=True, max_length=10), ), ] diff --git a/src/core/migrations/0082_auto_20230515_1706.py b/src/core/migrations/0082_auto_20230515_1706.py index c8e1f9cf2d..1a48c08e6e 100644 --- a/src/core/migrations/0082_auto_20230515_1706.py +++ b/src/core/migrations/0082_auto_20230515_1706.py @@ -4,43 +4,38 @@ def drop_ci_setting(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") Setting.objects.filter( - name='submission_competing_interests', + name="submission_competing_interests", ).delete() def create_ci_setting(apps, schema_editor): - SettingGroup = apps.get_model('core', 'SettingGroup') - Setting = apps.get_model('core', 'Setting') - Role = apps.get_model('core', 'Role') + SettingGroup = apps.get_model("core", "SettingGroup") + Setting = apps.get_model("core", "Setting") + Role = apps.get_model("core", "Role") - group = SettingGroup.objects.get(name='general') + group = SettingGroup.objects.get(name="general") setting, c = Setting.objects.get_or_create( group=group, - name='submission_competing_interests', + name="submission_competing_interests", defaults={ - 'pretty_name': "Enable Competing Interests", - 'types': 'boolean', - 'description': 'Enables the CI submission field.', - 'is_translatable': False, + "pretty_name": "Enable Competing Interests", + "types": "boolean", + "description": "Enables the CI submission field.", + "is_translatable": False, }, ) - roles = Role.objects.filter( - slug__in=['editor', 'journal-manager'] - ) + roles = Role.objects.filter(slug__in=["editor", "journal-manager"]) setting.editable_by.add(*roles) class Migration(migrations.Migration): - dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), + ("core", "0081_alter_account_preferred_timezone"), ] operations = [ - migrations.RunPython( - drop_ci_setting, - reverse_code=create_ci_setting), - ] \ No newline at end of file + migrations.RunPython(drop_ci_setting, reverse_code=create_ci_setting), + ] diff --git a/src/core/migrations/0082_galley_file_not_nullable.py b/src/core/migrations/0082_galley_file_not_nullable.py index 9fc04c0dc6..fd4ebb091e 100644 --- a/src/core/migrations/0082_galley_file_not_nullable.py +++ b/src/core/migrations/0082_galley_file_not_nullable.py @@ -3,22 +3,26 @@ from django.db import migrations, models import django.db.models.deletion + def upgrade(apps, schema_editor): Galley = apps.get_model("core", "Galley") Galley.objects.filter(file__isnull=True).delete() + class Migration(migrations.Migration): atomic = False dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), + ("core", "0081_alter_account_preferred_timezone"), ] operations = [ migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop), migrations.AlterField( - model_name='galley', - name='file', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.file'), + model_name="galley", + name="file", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.file" + ), ), ] diff --git a/src/core/migrations/0082_mark_safe_article_title.py b/src/core/migrations/0082_mark_safe_article_title.py index 595ea18654..0e2f21463b 100755 --- a/src/core/migrations/0082_mark_safe_article_title.py +++ b/src/core/migrations/0082_mark_safe_article_title.py @@ -6,29 +6,34 @@ REGEX = re.compile("{{\ ?([a-z\._-]*)article.title\ ?}}") OUTPUT_FMT = "{{ %sarticle.safe_title }}" -def replace_matches( match): + +def replace_matches(match): prefix = match.group(1) return OUTPUT_FMT % prefix + def replace_article_title(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') - setting_values= SettingValue.objects.all() + SettingValue = apps.get_model("core", "SettingValue") + setting_values = SettingValue.objects.all() for setting in setting_values: for language in settings.LANGUAGES: - value_string = 'value_{}'.format(language[0]) + value_string = "value_{}".format(language[0]) if setting and getattr(setting, value_string, None): setattr( - setting, value_string, - re.sub(REGEX, replace_matches, getattr(setting, value_string)) + setting, + value_string, + re.sub(REGEX, replace_matches, getattr(setting, value_string)), ) setting.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), + ("core", "0081_alter_account_preferred_timezone"), ] operations = [ - migrations.RunPython(replace_article_title, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + replace_article_title, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0083_press_editorial_team_20240116.py b/src/core/migrations/0083_press_editorial_team_20240116.py index 9c58079475..d4acb01e39 100644 --- a/src/core/migrations/0083_press_editorial_team_20240116.py +++ b/src/core/migrations/0083_press_editorial_team_20240116.py @@ -18,44 +18,48 @@ class Migration(migrations.Migration): atomic = False dependencies = [ - ('journal', '0059_alter_prepublicationchecklistitem_completed_by'), - ('press', '0031_staffgroup_staffgroupmember'), - ('core', '0082_account_name_prefix_20231204_1231'), + ("journal", "0059_alter_prepublicationchecklistitem_completed_by"), + ("press", "0031_staffgroup_staffgroupmember"), + ("core", "0082_account_name_prefix_20231204_1231"), ] operations = [ migrations.AddField( - model_name='editorialgroup', - name='press', + model_name="editorialgroup", + name="press", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='press.press', + to="press.press", null=True, ), ), - migrations.RunPython(set_default_press_id, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + set_default_press_id, reverse_code=migrations.RunPython.noop + ), migrations.AlterField( - model_name='editorialgroup', - name='press', + model_name="editorialgroup", + name="press", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='press.press', + to="press.press", default=core.models.default_press_id, ), ), migrations.AlterField( - model_name='editorialgroup', - name='journal', + model_name="editorialgroup", + name="journal", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to='journal.journal' + to="journal.journal", ), ), migrations.AddField( - model_name='editorialgroupmember', - name='statement', - field=models.TextField(blank=True, help_text='A statement of interest or purpose'), + model_name="editorialgroupmember", + name="statement", + field=models.TextField( + blank=True, help_text="A statement of interest or purpose" + ), ), ] diff --git a/src/core/migrations/0083_update_spelling.py b/src/core/migrations/0083_update_spelling.py index e91d5dd5b0..9d270a6709 100644 --- a/src/core/migrations/0083_update_spelling.py +++ b/src/core/migrations/0083_update_spelling.py @@ -11,8 +11,8 @@ def replace_settings(apps, schema_editor): - Setting = apps.get_model('core', 'Setting') - SettingValue = apps.get_model('core', 'SettingValue') + Setting = apps.get_model("core", "Setting") + SettingValue = apps.get_model("core", "SettingValue") for setting_name, description in DESCRIPTIONS_TO_FIX.items(): settings = Setting.objects.filter( @@ -26,12 +26,11 @@ def replace_settings(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0082_galley_file_not_nullable'), - ('core', '0082_mark_safe_article_title'), - ('core', '0082_galley_file_not_nullable'), - ('core', '0082_auto_20230515_1706'), + ("core", "0082_galley_file_not_nullable"), + ("core", "0082_mark_safe_article_title"), + ("core", "0082_galley_file_not_nullable"), + ("core", "0082_auto_20230515_1706"), ] operations = [ diff --git a/src/core/migrations/0084_xslt_1-5-1.py b/src/core/migrations/0084_xslt_1-5-1.py index 5eaf256afd..adf8e2c709 100644 --- a/src/core/migrations/0084_xslt_1-5-1.py +++ b/src/core/migrations/0084_xslt_1-5-1.py @@ -30,7 +30,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -51,7 +51,7 @@ def upgrade(apps, schema_editor): not XSLFile.objects.filter(label=LATEST_LABEL).exists() and LATEST_LABEL == settings.DEFAULT_XSL_FILE_LABEL ): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = FILE_NAME @@ -69,11 +69,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0083_update_spelling'), + ("core", "0083_update_spelling"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0085_auto_20240207_1654.py b/src/core/migrations/0085_auto_20240207_1654.py index 1b58cd2ed6..b87547c3b3 100644 --- a/src/core/migrations/0085_auto_20240207_1654.py +++ b/src/core/migrations/0085_auto_20240207_1654.py @@ -6,45 +6,682 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0084_xslt_1-5-1'), + ("core", "0084_xslt_1-5-1"), ] operations = [ migrations.AlterField( - model_name='account', - name='confirmation_code', - field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Confirmation Code'), + model_name="account", + name="confirmation_code", + field=models.CharField( + blank=True, max_length=200, null=True, verbose_name="Confirmation Code" + ), ), migrations.AlterField( - model_name='account', - name='enable_digest', - field=models.BooleanField(default=False, verbose_name='Enable Digest'), + model_name="account", + name="enable_digest", + field=models.BooleanField(default=False, verbose_name="Enable Digest"), ), migrations.AlterField( - model_name='account', - name='enable_public_profile', - field=models.BooleanField(default=False, help_text='If enabled, your basic profile will be available to the public.', verbose_name='Enable public profile'), + model_name="account", + name="enable_public_profile", + field=models.BooleanField( + default=False, + help_text="If enabled, your basic profile will be available to the public.", + verbose_name="Enable public profile", + ), ), migrations.AlterField( - model_name='account', - name='preferred_timezone', - field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], max_length=300, null=True, verbose_name='Preferred Timezone'), + model_name="account", + name="preferred_timezone", + field=models.CharField( + blank=True, + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + max_length=300, + null=True, + verbose_name="Preferred Timezone", + ), ), migrations.AlterField( - model_name='account', - name='profile_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=core.models.profile_images_upload_path, verbose_name='Profile Image'), + model_name="account", + name="profile_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=core.models.profile_images_upload_path, + verbose_name="Profile Image", + ), ), migrations.AlterField( - model_name='account', - name='salutation', - field=models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, null=True, verbose_name='Salutation'), + model_name="account", + name="salutation", + field=models.CharField( + blank=True, + choices=[ + ("Miss", "Miss"), + ("Ms", "Ms"), + ("Mrs", "Mrs"), + ("Mr", "Mr"), + ("Mx", "Mx"), + ("Dr", "Dr"), + ("Prof.", "Prof."), + ], + max_length=10, + null=True, + verbose_name="Salutation", + ), ), migrations.AlterField( - model_name='account', - name='signature', - field=models.TextField(blank=True, null=True, verbose_name='Signature'), + model_name="account", + name="signature", + field=models.TextField(blank=True, null=True, verbose_name="Signature"), ), ] diff --git a/src/core/migrations/0085_merge_20240201_1108.py b/src/core/migrations/0085_merge_20240201_1108.py index 9875ac0735..6995bffee7 100644 --- a/src/core/migrations/0085_merge_20240201_1108.py +++ b/src/core/migrations/0085_merge_20240201_1108.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0083_press_editorial_team_20240116'), - ('core', '0084_xslt_1-5-1'), + ("core", "0083_press_editorial_team_20240116"), + ("core", "0084_xslt_1-5-1"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0086_auto_20240312_0922.py b/src/core/migrations/0086_auto_20240312_0922.py index 735c8578af..696ba19a5d 100644 --- a/src/core/migrations/0086_auto_20240312_0922.py +++ b/src/core/migrations/0086_auto_20240312_0922.py @@ -5,125 +5,135 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0085_auto_20240207_1654'), + ("core", "0085_auto_20240207_1654"), ] operations = [ migrations.AlterField( - model_name='accessrequest', - name='evaluation_note', - field=core.model_utils.JanewayBleachField(help_text='This note will be sent to the requester when you approve or decline their request.', null=True), + model_name="accessrequest", + name="evaluation_note", + field=core.model_utils.JanewayBleachField( + help_text="This note will be sent to the requester when you approve or decline their request.", + null=True, + ), ), migrations.AlterField( - model_name='accessrequest', - name='text', + model_name="accessrequest", + name="text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='account', - name='biography', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Biography'), + model_name="account", + name="biography", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Biography" + ), ), migrations.AlterField( - model_name='account', - name='signature', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Signature'), + model_name="account", + name="signature", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Signature" + ), ), migrations.AlterField( - model_name='contact', - name='body', - field=core.model_utils.JanewayBleachField(verbose_name='Your message'), + model_name="contact", + name="body", + field=core.model_utils.JanewayBleachField(verbose_name="Your message"), ), migrations.AlterField( - model_name='editorialgroup', - name='description', + model_name="editorialgroup", + name="description", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_cy', + model_name="editorialgroup", + name="description_cy", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_de', + model_name="editorialgroup", + name="description_de", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_en', + model_name="editorialgroup", + name="description_en", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_en_us', + model_name="editorialgroup", + name="description_en_us", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_fr', + model_name="editorialgroup", + name="description_fr", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='editorialgroup', - name='description_nl', + model_name="editorialgroup", + name="description_nl", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='file', - name='description', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Description'), + model_name="file", + name="description", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Description" + ), ), migrations.AlterField( - model_name='filehistory', - name='description', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Description'), + model_name="filehistory", + name="description", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Description" + ), ), migrations.AlterField( - model_name='settingvalue', - name='value', + model_name="settingvalue", + name="value", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_cy', + model_name="settingvalue", + name="value_cy", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_de', + model_name="settingvalue", + name="value_de", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_en', + model_name="settingvalue", + name="value_en", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_en_us', + model_name="settingvalue", + name="value_en_us", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_fr', + model_name="settingvalue", + name="value_fr", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_nl', + model_name="settingvalue", + name="value_nl", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='task', - name='description', + model_name="task", + name="description", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='xslfile', - name='comments', + model_name="xslfile", + name="comments", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), ] diff --git a/src/core/migrations/0086_merge_20240307_1832.py b/src/core/migrations/0086_merge_20240307_1832.py index ee89a508ff..68a01bdc0b 100644 --- a/src/core/migrations/0086_merge_20240307_1832.py +++ b/src/core/migrations/0086_merge_20240307_1832.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0085_auto_20240207_1654'), - ('core', '0085_merge_20240201_1108'), + ("core", "0085_auto_20240207_1654"), + ("core", "0085_merge_20240201_1108"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0087_merge_20240315_1649.py b/src/core/migrations/0087_merge_20240315_1649.py index c92d786020..0a6a392072 100644 --- a/src/core/migrations/0087_merge_20240315_1649.py +++ b/src/core/migrations/0087_merge_20240315_1649.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0085_merge_20240201_1108'), - ('core', '0086_auto_20240312_0922'), + ("core", "0085_merge_20240201_1108"), + ("core", "0086_auto_20240312_0922"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0088_fix_untyped_galleys.py b/src/core/migrations/0088_fix_untyped_galleys.py index 1a65c0abf4..bb181f4eb8 100644 --- a/src/core/migrations/0088_fix_untyped_galleys.py +++ b/src/core/migrations/0088_fix_untyped_galleys.py @@ -5,11 +5,13 @@ def fix_untyped_galleys(apps, schema_editor): - """ Fixes galleys that haven't had a type set and linked metrics records""" + """Fixes galleys that haven't had a type set and linked metrics records""" - Galley = apps.get_model('core', 'Galley') - ArticleAccess = apps.get_model('metrics','ArticleAccess') - for galley in Galley.objects.filter(type="", file__isnull=False, article__isnull=False): + Galley = apps.get_model("core", "Galley") + ArticleAccess = apps.get_model("metrics", "ArticleAccess") + for galley in Galley.objects.filter( + type="", file__isnull=False, article__isnull=False + ): _, galley_type = get_galley_label_and_type(galley.file) galley.galley_type = galley_type galley.save() @@ -21,12 +23,13 @@ def fix_untyped_galleys(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0087_merge_20240315_1649'), - ('metrics', '0010_auto_20230317_1534'), + ("core", "0087_merge_20240315_1649"), + ("metrics", "0010_auto_20230317_1534"), ] operations = [ - migrations.RunPython(fix_untyped_galleys, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + fix_untyped_galleys, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/core/migrations/0088_merge_20240327_1910.py b/src/core/migrations/0088_merge_20240327_1910.py index 9f4423e79e..10fb2f3cf9 100644 --- a/src/core/migrations/0088_merge_20240327_1910.py +++ b/src/core/migrations/0088_merge_20240327_1910.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0086_merge_20240307_1832'), - ('core', '0087_merge_20240315_1649'), + ("core", "0086_merge_20240307_1832"), + ("core", "0087_merge_20240315_1649"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0089_auto_20240328_1213.py b/src/core/migrations/0089_auto_20240328_1213.py index 07af1d672d..a7712d19c8 100644 --- a/src/core/migrations/0089_auto_20240328_1213.py +++ b/src/core/migrations/0089_auto_20240328_1213.py @@ -8,10 +8,10 @@ def set_editorial_group_image_dispay(apps, schema): SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( - setting__name='enable_editorial_images', + setting__name="enable_editorial_images", ) for sv in setting_values: - if sv.journal and sv.value in ['on', 'On']: + if sv.journal and sv.value in ["on", "On"]: EditorialGroup.objects.filter( journal=sv.journal, ).update( @@ -20,16 +20,18 @@ def set_editorial_group_image_dispay(apps, schema): class Migration(migrations.Migration): - dependencies = [ - ('core', '0088_merge_20240327_1910'), + ("core", "0088_merge_20240327_1910"), ] operations = [ migrations.AddField( - model_name='editorialgroup', - name='display_profile_images', - field=models.BooleanField(default=False, help_text='Enable to display profile images for this group.'), + model_name="editorialgroup", + name="display_profile_images", + field=models.BooleanField( + default=False, + help_text="Enable to display profile images for this group.", + ), ), migrations.RunPython( set_editorial_group_image_dispay, diff --git a/src/core/migrations/0089_auto_20240424_1005.py b/src/core/migrations/0089_auto_20240424_1005.py index 7de122897f..2781a5f0df 100644 --- a/src/core/migrations/0089_auto_20240424_1005.py +++ b/src/core/migrations/0089_auto_20240424_1005.py @@ -23,8 +23,8 @@ def create_past_reviewer_setting(apps, schema_editor): types="boolean", pretty_name="Display List of Reviewers from Past Review Rounds", description="When this setting is enabled the review assignment page " - "will show a table of reviewers who have completed a " - "review in past review rounds." + "will show a table of reviewers who have completed a " + "review in past review rounds.", ) SettingValue.objects.create( setting=setting, @@ -33,14 +33,12 @@ def create_past_reviewer_setting(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0088_merge_20240327_1910'), + ("core", "0088_merge_20240327_1910"), ] operations = [ migrations.RunPython( - drop_past_reviewer_setting, - reverse_code=create_past_reviewer_setting + drop_past_reviewer_setting, reverse_code=create_past_reviewer_setting ), ] diff --git a/src/core/migrations/0089_update_setting_types.py b/src/core/migrations/0089_update_setting_types.py index 9362f7894d..d4e0c3b61b 100644 --- a/src/core/migrations/0089_update_setting_types.py +++ b/src/core/migrations/0089_update_setting_types.py @@ -5,25 +5,25 @@ SETTINGS_WITH_OLD_AND_NEW_TYPES = [ - ('general', 'collection_name', 'text', 'char'), - ('general', 'collection_name_plural', 'text', 'char'), - ('general', 'matromo_tracking_code', 'text', 'char'), - ('general', 'copyright_submission_label', 'char', 'mini-html'), - ('crossref', 'crossref_date_suffix', 'text', 'char'), - ('general', 'default_journal_language', 'text', 'char'), + ("general", "collection_name", "text", "char"), + ("general", "collection_name_plural", "text", "char"), + ("general", "matromo_tracking_code", "text", "char"), + ("general", "copyright_submission_label", "char", "mini-html"), + ("crossref", "crossref_date_suffix", "text", "char"), + ("general", "default_journal_language", "text", "char"), ] EMAIL_SUBJECT_GROUP_WITH_OLD_AND_NEW_TYPE = [ - ('email_subject', 'text', 'char'), - ('email_subject', 'rich-text', 'char'), + ("email_subject", "text", "char"), + ("email_subject", "rich-text", "char"), ] def standardize_setting_types(apps, schema_editor): - """ Standardizes use of text, char, and mini-html setting types""" + """Standardizes use of text, char, and mini-html setting types""" - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") for group, name, old_type, new_type in SETTINGS_WITH_OLD_AND_NEW_TYPES: update_setting_types( Setting, @@ -33,38 +33,36 @@ def standardize_setting_types(apps, schema_editor): setting_name=name, ) - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") for group, old_type, new_type in EMAIL_SUBJECT_GROUP_WITH_OLD_AND_NEW_TYPE: update_setting_types(Setting, group, old_type, new_type) class Migration(migrations.Migration): - dependencies = [ - ('core', '0088_fix_untyped_galleys'), + ("core", "0088_fix_untyped_galleys"), ] operations = [ migrations.AlterField( - model_name='setting', - name='types', + model_name="setting", + name="types", field=models.CharField( choices=[ - ('rich-text', 'Rich Text'), - ('mini-html', 'Mini HTML'), - ('text', 'Plain Text'), - ('char', 'Characters'), - ('number', 'Number'), - ('boolean', 'Boolean'), - ('file', 'File'), - ('select', 'Select'), - ('json', 'JSON') + ("rich-text", "Rich Text"), + ("mini-html", "Mini HTML"), + ("text", "Plain Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), ], max_length=20, ), ), migrations.RunPython( - standardize_setting_types, - reverse_code=migrations.RunPython.noop + standardize_setting_types, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/core/migrations/0090_editorial_team_page_i18n.py b/src/core/migrations/0090_editorial_team_page_i18n.py index 7c0b63e827..bc709b3d2b 100644 --- a/src/core/migrations/0090_editorial_team_page_i18n.py +++ b/src/core/migrations/0090_editorial_team_page_i18n.py @@ -4,22 +4,21 @@ def add_translations_to_db(apps, schema_editor): - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") svs_to_update = SettingValue.objects.filter( - setting__name='editorial_group_page_name', + setting__name="editorial_group_page_name", ) for sv in svs_to_update: - sv.value_fr = 'Équipe éditoriale' - sv.value_de = 'Redaktionsteam' - sv.value_nl = 'Redactie' - sv.value_cy = 'Tîm Golygyddol' + sv.value_fr = "Équipe éditoriale" + sv.value_de = "Redaktionsteam" + sv.value_nl = "Redactie" + sv.value_cy = "Tîm Golygyddol" sv.save() class Migration(migrations.Migration): - dependencies = [ - ('core', '0089_auto_20240328_1213'), + ("core", "0089_auto_20240328_1213"), ] operations = [ diff --git a/src/core/migrations/0090_setting_value.py b/src/core/migrations/0090_setting_value.py index 2e0480b301..28defd684e 100644 --- a/src/core/migrations/0090_setting_value.py +++ b/src/core/migrations/0090_setting_value.py @@ -4,45 +4,44 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0089_update_setting_types'), + ("core", "0089_update_setting_types"), ] operations = [ migrations.AlterField( - model_name='settingvalue', - name='value', + model_name="settingvalue", + name="value", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_cy', + model_name="settingvalue", + name="value_cy", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_de', + model_name="settingvalue", + name="value_de", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_en', + model_name="settingvalue", + name="value_en", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_en_us', + model_name="settingvalue", + name="value_en_us", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_fr', + model_name="settingvalue", + name="value_fr", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='settingvalue', - name='value_nl', + model_name="settingvalue", + name="value_nl", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/core/migrations/0091_fix_journal_name_amp.py b/src/core/migrations/0091_fix_journal_name_amp.py index 579267d63b..467a668f36 100644 --- a/src/core/migrations/0091_fix_journal_name_amp.py +++ b/src/core/migrations/0091_fix_journal_name_amp.py @@ -5,32 +5,29 @@ HTML_ENTITIES_TO_FIX = [ - ('&', '&'), - ('<', '<'), - ('>', '>'), + ("&", "&"), + ("<", "<"), + (">", ">"), ] def repair_entities_in_journal_name(apps, schema_editor): - replace_strings_in_setting_values( apps, - 'journal_name', - 'general', + "journal_name", + "general", HTML_ENTITIES_TO_FIX, - languages=['cy', 'de', 'en', 'en-us', 'en_us', 'es', 'fr', 'it', 'nl'], + languages=["cy", "de", "en", "en-us", "en_us", "es", "fr", "it", "nl"], ) class Migration(migrations.Migration): - dependencies = [ - ('core', '0090_setting_value'), + ("core", "0090_setting_value"), ] operations = [ migrations.RunPython( - repair_entities_in_journal_name, - reverse_code=migrations.RunPython.noop + repair_entities_in_journal_name, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/core/migrations/0091_merge_20240425_1555.py b/src/core/migrations/0091_merge_20240425_1555.py index 2f66f3d0a9..dc702f50c0 100644 --- a/src/core/migrations/0091_merge_20240425_1555.py +++ b/src/core/migrations/0091_merge_20240425_1555.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0089_auto_20240424_1005'), - ('core', '0090_editorial_team_page_i18n'), + ("core", "0089_auto_20240424_1005"), + ("core", "0090_editorial_team_page_i18n"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0092_xslt_1-6-0.py b/src/core/migrations/0092_xslt_1-6-0.py index d1f33fcbcd..d20d0e3e30 100644 --- a/src/core/migrations/0092_xslt_1-6-0.py +++ b/src/core/migrations/0092_xslt_1-6-0.py @@ -18,7 +18,7 @@ def upgrade(apps, schema_editor): - """ Installs the latest default XSLT preserving the previous one + """Installs the latest default XSLT preserving the previous one Only runs if the previous version XSLT was installed. If it was, it relabels it (the old label had no version), installs @@ -39,7 +39,7 @@ def upgrade(apps, schema_editor): not XSLFile.objects.filter(label=LATEST_LABEL).exists() and LATEST_LABEL == settings.DEFAULT_XSL_FILE_LABEL ): - with open(xsl_path, 'rb') as f: + with open(xsl_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = FILE_NAME @@ -57,11 +57,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('core', '0091_merge_20240425_1555'), + ("core", "0091_merge_20240425_1555"), ] - operations = [ - migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop) - ] + operations = [migrations.RunPython(upgrade, reverse_code=migrations.RunPython.noop)] diff --git a/src/core/migrations/0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0.py b/src/core/migrations/0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0.py index 691c38b2bf..f0480d75d7 100644 --- a/src/core/migrations/0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0.py +++ b/src/core/migrations/0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0088_fix_untyped_galleys'), - ('core', '0092_xslt_1-6-0'), + ("core", "0088_fix_untyped_galleys"), + ("core", "0092_xslt_1-6-0"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0094_alter_account_preferred_timezone.py b/src/core/migrations/0094_alter_account_preferred_timezone.py index a6223aeeab..7cdc98ee39 100644 --- a/src/core/migrations/0094_alter_account_preferred_timezone.py +++ b/src/core/migrations/0094_alter_account_preferred_timezone.py @@ -5,15 +5,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0'), + ("core", "0093_merge_0088_fix_untyped_galleys_0092_xslt_1-6-0"), ] operations = [ migrations.AlterField( - model_name='account', - name='preferred_timezone', - field=core.model_utils.DynamicChoiceField(blank=True, choices=[], max_length=300, null=True, verbose_name='Preferred Timezone'), + model_name="account", + name="preferred_timezone", + field=core.model_utils.DynamicChoiceField( + blank=True, + choices=[], + max_length=300, + null=True, + verbose_name="Preferred Timezone", + ), ), ] diff --git a/src/core/migrations/0095_account_fields_null_to_string.py b/src/core/migrations/0095_account_fields_null_to_string.py index 3497568017..6d86396dd7 100644 --- a/src/core/migrations/0095_account_fields_null_to_string.py +++ b/src/core/migrations/0095_account_fields_null_to_string.py @@ -6,30 +6,28 @@ def migrate_null_values(apps, schema_editor): - model = apps.get_model('core', 'Account') + model = apps.get_model("core", "Account") fields = [ - 'biography', - 'department', - 'first_name', - 'middle_name', - 'last_name', - 'institution', - 'salutation', - 'signature', - 'suffix', + "biography", + "department", + "first_name", + "middle_name", + "last_name", + "institution", + "salutation", + "signature", + "suffix", ] migration_utils.store_empty_strings(model, fields) class Migration(migrations.Migration): - dependencies = [ - ('core', '0094_alter_account_preferred_timezone'), + ("core", "0094_alter_account_preferred_timezone"), ] operations = [ migrations.RunPython( - migrate_null_values, - reverse_code=migrations.RunPython.noop + migrate_null_values, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/core/migrations/0095_auto_20240605_1024.py b/src/core/migrations/0095_auto_20240605_1024.py index 8d0d4dd0fe..9e1948d21c 100644 --- a/src/core/migrations/0095_auto_20240605_1024.py +++ b/src/core/migrations/0095_auto_20240605_1024.py @@ -12,8 +12,7 @@ { "name": "subject_notification_acceptance", "group_name": "email_subject", - } - + }, ] @@ -21,15 +20,14 @@ def delete_notification_acceptance_setting(apps, schema_editor): for setting in settings: migration_utils.delete_setting( apps=apps, - setting_group_name=setting.get('group_name'), - setting_name=setting.get('name'), + setting_group_name=setting.get("group_name"), + setting_name=setting.get("name"), ) - class Migration(migrations.Migration): dependencies = [ - ('core', '0095_merge_20240621_0722'), + ("core", "0095_merge_20240621_0722"), ] operations = [ diff --git a/src/core/migrations/0095_merge_20240621_0722.py b/src/core/migrations/0095_merge_20240621_0722.py index 1d336c4687..b3a16241d8 100644 --- a/src/core/migrations/0095_merge_20240621_0722.py +++ b/src/core/migrations/0095_merge_20240621_0722.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0091_fix_journal_name_amp'), - ('core', '0094_alter_account_preferred_timezone'), + ("core", "0091_fix_journal_name_amp"), + ("core", "0094_alter_account_preferred_timezone"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0096_alter_account_activation_code_and_more.py b/src/core/migrations/0096_alter_account_activation_code_and_more.py index e9221a26f0..51f5cefd30 100644 --- a/src/core/migrations/0096_alter_account_activation_code_and_more.py +++ b/src/core/migrations/0096_alter_account_activation_code_and_more.py @@ -7,55 +7,100 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0095_account_fields_null_to_string'), + ("core", "0095_account_fields_null_to_string"), ] operations = [ migrations.AlterField( - model_name='account', - name='biography', - field=core.model_utils.JanewayBleachField(blank=True, verbose_name='Biography'), + model_name="account", + name="biography", + field=core.model_utils.JanewayBleachField( + blank=True, verbose_name="Biography" + ), ), migrations.AlterField( - model_name='account', - name='department', - field=models.CharField(blank=True, max_length=300, verbose_name='Department', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="department", + field=models.CharField( + blank=True, + max_length=300, + verbose_name="Department", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='first_name', - field=models.CharField(max_length=300, verbose_name='First name', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="first_name", + field=models.CharField( + max_length=300, + verbose_name="First name", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='institution', - field=models.CharField(blank=True, max_length=1000, verbose_name='Institution', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="institution", + field=models.CharField( + blank=True, + max_length=1000, + verbose_name="Institution", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='last_name', - field=models.CharField(max_length=300, verbose_name='Last name', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="last_name", + field=models.CharField( + max_length=300, + verbose_name="Last name", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='middle_name', - field=models.CharField(blank=True, max_length=300, verbose_name='Middle name', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="middle_name", + field=models.CharField( + blank=True, + max_length=300, + verbose_name="Middle name", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='salutation', - field=models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, verbose_name='Salutation', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="salutation", + field=models.CharField( + blank=True, + choices=[ + ("Miss", "Miss"), + ("Ms", "Ms"), + ("Mrs", "Mrs"), + ("Mr", "Mr"), + ("Mx", "Mx"), + ("Dr", "Dr"), + ("Prof.", "Prof."), + ], + max_length=10, + verbose_name="Salutation", + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='account', - name='signature', - field=core.model_utils.JanewayBleachField(blank=True, verbose_name='Signature'), + model_name="account", + name="signature", + field=core.model_utils.JanewayBleachField( + blank=True, verbose_name="Signature" + ), ), migrations.AlterField( - model_name='account', - name='suffix', - field=models.CharField(blank=True, max_length=300, verbose_name='Name suffix', validators=[utils.forms.plain_text_validator]), + model_name="account", + name="suffix", + field=models.CharField( + blank=True, + max_length=300, + verbose_name="Name suffix", + validators=[utils.forms.plain_text_validator], + ), ), ] diff --git a/src/core/migrations/0096_update_review_ack_email.py b/src/core/migrations/0096_update_review_ack_email.py index 7828abbb32..3c508fe24f 100644 --- a/src/core/migrations/0096_update_review_ack_email.py +++ b/src/core/migrations/0096_update_review_ack_email.py @@ -15,17 +15,23 @@ def replace_review_completed_ack(apps, schema_editor): migration_utils.update_default_setting_values( apps, - setting_name='review_complete_acknowledgement', - group_name='email', - values_to_replace=[OLD_VALUE_ONE, OLD_VALUE_TWO, OLD_VALUE_THREE, OLD_VALUE_FOUR, OLD_VALUE_FIVE, OLD_VALUE_SIX], + setting_name="review_complete_acknowledgement", + group_name="email", + values_to_replace=[ + OLD_VALUE_ONE, + OLD_VALUE_TWO, + OLD_VALUE_THREE, + OLD_VALUE_FOUR, + OLD_VALUE_FIVE, + OLD_VALUE_SIX, + ], replacement_value=NEW_VALUE, ) class Migration(migrations.Migration): - dependencies = [ - ('core', '0095_auto_20240605_1024'), + ("core", "0095_auto_20240605_1024"), ] operations = [ diff --git a/src/core/migrations/0097_merge_20240819_1617.py b/src/core/migrations/0097_merge_20240819_1617.py index 70907519f3..793b4fccc3 100644 --- a/src/core/migrations/0097_merge_20240819_1617.py +++ b/src/core/migrations/0097_merge_20240819_1617.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0096_alter_account_activation_code_and_more'), - ('core', '0096_update_review_ack_email'), + ("core", "0096_alter_account_activation_code_and_more"), + ("core", "0096_update_review_ack_email"), ] - operations = [ - ] + operations = [] diff --git a/src/core/migrations/0098_add_pii_filter.py b/src/core/migrations/0098_add_pii_filter.py index 207b751cb4..b991d89af4 100644 --- a/src/core/migrations/0098_add_pii_filter.py +++ b/src/core/migrations/0098_add_pii_filter.py @@ -5,36 +5,44 @@ updates = [ { - 'setting_names': ['review_decision_accept', 'request_revisions', 'review_decision_decline'], - 'replacements': [( - '{{ article.correspondence_author.full_name }}', - '{{ article.correspondence_author.full_name|se_can_see_pii:article }}' - )] + "setting_names": [ + "review_decision_accept", + "request_revisions", + "review_decision_decline", + ], + "replacements": [ + ( + "{{ article.correspondence_author.full_name }}", + "{{ article.correspondence_author.full_name|se_can_see_pii:article }}", + ) + ], }, { - 'setting_names': ['revisions_complete_editor_notification'], - 'replacements': [( - '{{ revision.article.correspondence_author.full_name }}', - '{{ revision.article.correspondence_author.full_name|se_can_see_pii:revision.article }}' - )] + "setting_names": ["revisions_complete_editor_notification"], + "replacements": [ + ( + "{{ revision.article.correspondence_author.full_name }}", + "{{ revision.article.correspondence_author.full_name|se_can_see_pii:revision.article }}", + ) + ], }, ] def replace_values(apps, schema_editor): for update in updates: - for setting_name in update.get('setting_names'): + for setting_name in update.get("setting_names"): migration_utils.replace_strings_in_setting_values( apps=apps, setting_name=setting_name, - group_name='email', - replacements=update.get('replacements'), + group_name="email", + replacements=update.get("replacements"), ) class Migration(migrations.Migration): dependencies = [ - ('core', '0097_merge_20240819_1617'), + ("core", "0097_merge_20240819_1617"), ] operations = [ diff --git a/src/core/migrations/0099_alter_accountrole_options.py b/src/core/migrations/0099_alter_accountrole_options.py index 434f7132ed..724b1d6b4e 100644 --- a/src/core/migrations/0099_alter_accountrole_options.py +++ b/src/core/migrations/0099_alter_accountrole_options.py @@ -4,14 +4,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0098_add_pii_filter'), + ("core", "0098_add_pii_filter"), ] operations = [ migrations.AlterModelOptions( - name='accountrole', - options={'ordering': ('journal', 'role')}, + name="accountrole", + options={"ordering": ("journal", "role")}, ), ] diff --git a/src/core/model_utils.py b/src/core/model_utils.py index 48733dbe7d..b35faf01dd 100644 --- a/src/core/model_utils.py +++ b/src/core/model_utils.py @@ -1,6 +1,7 @@ """ Utilities for designing and working with models """ + __copyright__ = "Copyright 2018 Birkbeck, University of London" __author__ = "Birkbeck Centre for Technology and Publishing" __license__ = "AGPL v3" @@ -21,7 +22,7 @@ ) from django.core import validators from django.core.exceptions import ValidationError -from django.db import( +from django.db import ( connection, IntegrityError, models, @@ -64,13 +65,13 @@ class AbstractSiteModel(models.Model): """Adds site-like functionality to any model""" + SCHEMES = { True: "https", False: "http", } - domain = models.CharField( - max_length=255, unique=True, blank=True, null=True) + domain = models.CharField(max_length=255, unique=True, blank=True, null=True) is_secure = models.BooleanField( default=False, help_text="If the site should redirect to HTTPS, mark this.", @@ -81,7 +82,7 @@ class Meta: @classmethod def get_by_request(cls, request): - """ Returns the site object relevant for the given request + """Returns the site object relevant for the given request :param request: A Django Request object :return: The site object and the path under which the object was matched """ @@ -105,9 +106,10 @@ def get_by_domain(cls, domain): obj = cls.objects.get(domain=domain) return obj - def site_url(self, path=None, query=''): + def site_url(self, path=None, query=""): # This is here to avoid circular imports from utils import logic + return logic.build_url( netloc=self.domain, scheme=self._get_scheme(), @@ -122,8 +124,9 @@ def _get_scheme(self): return scheme -class PGCaseInsensitivedMixin(): +class PGCaseInsensitivedMixin: """Activates the citext postgres extension for the given field""" + def db_type(self, connection): if connection.vendor == "postgresql": return "citext" @@ -138,7 +141,7 @@ class PGCaseInsensitiveEmailField(PGCaseInsensitivedMixin, models.EmailField): def merge_models(src, dest): - """ Moves relations from `src` to `dest` and deletes src + """Moves relations from `src` to `dest` and deletes src :param src: Model instance to be removed :param dest: Model instance into which src will be merged """ @@ -180,10 +183,9 @@ def merge_models(src, dest): class JanewayMultilingualQuerySet(MultilingualQuerySet): - def check_kwargs(self, **kwargs): for k, v in kwargs.items(): - if k.endswith('_{}'.format(settings.LANGUAGE_CODE)): + if k.endswith("_{}".format(settings.LANGUAGE_CODE)): return False return True @@ -191,8 +193,8 @@ def check_base_language(self, **kwargs): lang = translation.get_language() if lang and lang != settings.LANGUAGE_CODE and self.check_kwargs(**kwargs): raise Exception( - 'When creating a new translation you must provide' - ' a translation for the base language, {}'.format( + "When creating a new translation you must provide" + " a translation for the base language, {}".format( settings.LANGUAGE_CODE ) ) @@ -216,7 +218,7 @@ def get_queryset(self): class M2MOrderedThroughField(ManyToManyField): - """ Orders m2m related objects by their 'through' Model + """Orders m2m related objects by their 'through' Model When a 'through' model declares an ordering in its Meta options, it is ignored by Django's default manager. @@ -224,28 +226,32 @@ class M2MOrderedThroughField(ManyToManyField): of the manager so that if the through model declares an ordering logic, it will be used in the join query """ + def contribute_to_class(self, cls, *args, **kwargs): super_return = super().contribute_to_class(cls, *args, **kwargs) - setattr(cls, self.name, M2MOrderedThroughDescriptor(self.remote_field, reverse=False)) + setattr( + cls, + self.name, + M2MOrderedThroughDescriptor(self.remote_field, reverse=False), + ) return super_return class M2MOrderedThroughDescriptor(ManyToManyDescriptor): - @cached_property def related_manager_cls(self): related_model = self.rel.related_model if self.reverse else self.rel.model related_manager = create_forward_many_to_many_manager( - related_model._default_manager.__class__, - self.rel, - reverse=self.reverse, + related_model._default_manager.__class__, + self.rel, + reverse=self.reverse, ) return create_m2m_ordered_through_manager(related_manager, self.rel) @contextmanager def allow_m2m_operation(through): - """ Enables m2m operations on through models + """Enables m2m operations on through models This is done by flagging the model as auto_created dynamically. It only works if all your extra fields on the through model have defaults declared. @@ -271,7 +277,7 @@ def _apply_ordering(self, queryset): return queryset.extra(order_by=[related_name]) def get_queryset(self, *args, **kwargs): - """ Here is where we can finally apply our ordering logic""" + """Here is where we can finally apply our ordering logic""" qs = super().get_queryset(*args, **kwargs) return self._apply_ordering(qs) @@ -287,13 +293,12 @@ def clear(self): with allow_m2m_operation(rel.through): return super().clear() - return M2MOrderedThroughManager class SVGImageField(models.ImageField): def formfield(self, **kwargs): - defaults = {'form_class': SVGImageFieldForm} + defaults = {"form_class": SVGImageFieldForm} defaults.update(kwargs) return super().formfield(**defaults) @@ -316,13 +321,13 @@ def to_python(self, data): return None # Data can be a readable object, a templfile or a filepath - if hasattr(data, 'temporary_file_path'): + if hasattr(data, "temporary_file_path"): file_obj = data.temporary_file_path() else: - if hasattr(data, 'read'): + if hasattr(data, "read"): file_obj = BytesIO(data.read()) else: - file_obj = BytesIO(data['content']) + file_obj = BytesIO(data["content"]) try: # load() could spot a truncated JPEG, but it loads the entire @@ -337,10 +342,10 @@ def to_python(self, data): # Handle SVG here if not is_svg(file_obj): raise ValidationError( - self.error_messages['invalid_image'], - code='invalid_image', + self.error_messages["invalid_image"], + code="invalid_image", ).with_traceback(sys.exc_info()[2]) - if hasattr(super_result, 'seek') and callable(super_result.seek): + if hasattr(super_result, "seek") and callable(super_result.seek): super_result.seek(0) return super_result @@ -352,17 +357,17 @@ def is_svg(f): f.seek(0) tag = None try: - for event, el in et.iterparse(f, ('start',)): + for event, el in et.iterparse(f, ("start",)): tag = el.tag break except et.ParseError: pass - return tag == '{http://www.w3.org/2000/svg}svg' + return tag == "{http://www.w3.org/2000/svg}svg" class LastModifiedModelQuerySet(models.query.QuerySet): def update(self, *args, **kwargs): - kwargs['last_modified'] = timezone.now() + kwargs["last_modified"] = timezone.now() super().update(*args, **kwargs) @@ -377,10 +382,7 @@ class AbstractLastModifiedModel(models.Model): _LAST_MODIFIED_FIELDS_MAP = {} _LAST_MODIFIED_ACCESSORS = {} - last_modified = models.DateTimeField( - auto_now=True, - editable=True - ) + last_modified = models.DateTimeField(auto_now=True, editable=True) objects = LastModifiedModelManager() class Meta: @@ -392,7 +394,6 @@ def model_key(self): @classmethod def get_last_modified_field_map(cls, visited_fields=None): - # Early return of cached calculation if cls._LAST_MODIFIED_FIELDS_MAP: return cls._LAST_MODIFIED_FIELDS_MAP @@ -404,11 +405,8 @@ def get_last_modified_field_map(cls, visited_fields=None): local_fields = cls._meta.get_fields() for field in local_fields: if ( - (field.many_to_many - or field.one_to_many - or field.many_to_one) - and field not in visited_fields - ): + field.many_to_many or field.one_to_many or field.many_to_one + ) and field not in visited_fields: model = field.remote_field.model if issubclass(model, AbstractLastModifiedModel): # Avoid infinite recursion when models are doubly linked @@ -437,7 +435,7 @@ def get_last_modified_accessors(cls): # sqlite's MAX returns NULL if any value is NULL Coalesce( f"{field}__last_modified", - timezone.make_aware(timezone.datetime.fromtimestamp(0)) + timezone.make_aware(timezone.datetime.fromtimestamp(0)), ) for field in field_map.keys() ) @@ -445,9 +443,8 @@ def get_last_modified_accessors(cls): cls._LAST_MODIFIED_ACCESSORS = accessors return cls.get_last_modified_accessors() - def best_last_modified_date(self, visited_nodes=None): - """ Determines the last modified date considering all related objects + """Determines the last modified date considering all related objects Any relationship which is an instance of this class will have its `last_modified` date considered for calculating the last_modified date for the instance from which this method is called @@ -469,43 +466,44 @@ def best_last_modified_date(self, visited_nodes=None): class SearchLookup(PGSearchLookup): - """ A Search lookup that works across multiple databases. + """A Search lookup that works across multiple databases. Django dropped support for the search lookup when using MySQLin 1.10 This lookup attempts to restore some of that behaviour so that MySQL users can still benefit of some form of full text search. For any other vendors, the search performs a simple LIKE match. For Postgres, the behaviour from contrib.postgres.lookups.SearchLookup is preserved """ - lookup_name = 'search' + + lookup_name = "search" def as_mysql(self, compiler, connection): - lhs, lhs_params = self.process_lhs(compiler, connection) - rhs, rhs_params = self.process_rhs(compiler, connection) - params = lhs_params + rhs_params - return 'MATCH (%s) AGAINST (%s IN BOOLEAN MODE)' % (lhs, rhs), params + lhs, lhs_params = self.process_lhs(compiler, connection) + rhs, rhs_params = self.process_rhs(compiler, connection) + params = lhs_params + rhs_params + return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (lhs, rhs), params def as_postgresql(self, compiler, connection): return super().as_sql(compiler, connection) - def as_sql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params - return 'MATCH (%s) AGAINST (%s IN BOOLEAN MODE)' % (lhs, rhs), params + return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (lhs, rhs), params def process_lhs(self, compiler, connection): - if connection.vendor != 'postgresql': + if connection.vendor != "postgresql": return models.Lookup.process_lhs(self, compiler, connection) else: return super().process_lhs(compiler, connection) def process_rhs(self, compiler, connection): - if connection.vendor != 'postgresql': + if connection.vendor != "postgresql": return models.Lookup.process_rhs(self, compiler, connection) else: return super().process_rhs(compiler, connection) + SearchVectorField.register_lookup(SearchLookup) models.CharField.register_lookup(SearchLookup) @@ -522,16 +520,13 @@ def search(self, search_term, search_filters, sort=None, site=None): return self._search(search_term, search_filters, sort, site) def _search(self, search_term, search_filters, sort=None, site=None): - """ This is a copy of search from journal.views.old_search with filters - """ + """This is a copy of search from journal.views.old_search with filters""" articles = self.get_queryset() if search_term: escaped = re.escape(search_term) split_term = [re.escape(word) for word in search_term.split(" ")] split_term.append(escaped) - search_regex = "^({})$".format( - "|".join({name for name in split_term}) - ) + search_regex = "^({})$".format("|".join({name for name in split_term})) q_object = Q() if search_filters.get("title"): q_object = q_object | Q(title__icontains=search_term) @@ -541,8 +536,8 @@ def _search(self, search_term, search_filters, sort=None, site=None): q_object = q_object | Q(keywords__word=search_term) if search_filters.get("authors"): q_object = q_object | ( - Q(frozenauthor__first_name__iregex=search_regex) | - Q(frozenauthor__last_name__iregex=search_regex) + Q(frozenauthor__first_name__iregex=search_regex) + | Q(frozenauthor__last_name__iregex=search_regex) ) articles = articles.filter(q_object) if site: @@ -560,8 +555,9 @@ def mysql_search(self, search_term, search_filters, sort=None, site=None): def get_search_lookups(self): return self.search_lookups + class SearchVector(DjangoSearchVector): - """ An Extension of SearchVector that works with SearchVectorField + """An Extension of SearchVector that works with SearchVectorField Django's implementation assumes that the `to_tsvector` function needs to be called with the provided column, except that when the field is already @@ -571,15 +567,16 @@ class SearchVector(DjangoSearchVector): override under `set_source_expressions` """ + def set_source_expressions(self, _): - """ Ignore Django's implementation + """Ignore Django's implementation We don't require the expressions to be re-casted during the as_sql call """ pass # Override template to ignore function function = None - template = '%(expressions)s' + template = "%(expressions)s" def search_model_admin(request, model, q=None, queryset=None): @@ -593,7 +590,7 @@ def search_model_admin(request, model, q=None, queryset=None): :param queryset: a pre-existing queryset to filter by the search term """ if not q: - q = request.POST['q'] if request.POST else request.GET['q'] + q = request.POST["q"] if request.POST else request.GET["q"] if not queryset: queryset = model.objects.all() registered_admin = admin.site._registry[model] @@ -601,7 +598,7 @@ def search_model_admin(request, model, q=None, queryset=None): class JanewayBleachField(BleachField): - """ An override of BleachField to avoid casting SafeString from db + """An override of BleachField to avoid casting SafeString from db Bleachfield automatically casts the default return type (string) into a SafeString, which is okay when using the value for HTML rendering but not when using the value elsewhere (XML encoding) @@ -649,18 +646,18 @@ def __init__(self, *args, **kwargs): # they will be ignored by the Django Bleach implementation of # BleachField.formfield # https://github.com/marksweb/django-bleach/blob/d675d09423ddb440b4c83c8a82bd8b853f4603c7/django_bleach/models.py#L42-L61 - kwargs['allowed_tags'] = get_allowed_html_tags_minimal() - kwargs['allowed_attributes'] = get_allowed_attributes_minimal() - kwargs['widget'] = TinyMCE( + kwargs["allowed_tags"] = get_allowed_html_tags_minimal() + kwargs["allowed_attributes"] = get_allowed_attributes_minimal() + kwargs["widget"] = TinyMCE( mce_attrs={ - 'plugins': 'help code', - 'menubar': '', - 'forced_root_block': 'div', - 'toolbar': 'help removeformat | undo redo | ' \ - 'bold italic superscript subscript', - 'height': '8rem', - 'resize': True, - 'elementpath': False, + "plugins": "help code", + "menubar": "", + "forced_root_block": "div", + "toolbar": "help removeformat | undo redo | " + "bold italic superscript subscript", + "height": "8rem", + "resize": True, + "elementpath": False, } ) super().__init__(*args, **kwargs) @@ -673,7 +670,7 @@ class JanewayBleachCharField(JanewayBleachField): """ def formfield(self, *args, **kwargs): - defaults = {'form_class': MiniHTMLFormField} + defaults = {"form_class": MiniHTMLFormField} defaults.update(kwargs) return super().formfield(*args, **defaults) @@ -714,17 +711,15 @@ def validate(self, value, model_instance): except ValidationError as e: # If the raised exception is for invalid choice we check if the # choice is in dynamic choices. - if e.code == 'invalid_choice': - potential_values = set( - item[0] for item in self.dynamic_choices - ) + if e.code == "invalid_choice": + potential_values = set(item[0] for item in self.dynamic_choices) if value not in potential_values: raise class DateTimePickerInput(forms.DateTimeInput): - format_key = 'DATETIME_INPUT_FORMATS' - template_name = 'admin/core/widgets/datetimepicker.html' + format_key = "DATETIME_INPUT_FORMATS" + template_name = "admin/core/widgets/datetimepicker.html" class DateTimePickerFormField(forms.DateTimeField): @@ -733,9 +728,10 @@ class DateTimePickerFormField(forms.DateTimeField): class DateTimePickerModelField(models.DateTimeField): def formfield(self, **kwargs): - kwargs['form_class'] = DateTimePickerFormField + kwargs["form_class"] = DateTimePickerFormField return super().formfield(**kwargs) + @property def NotImplementedField(self): raise NotImplementedError diff --git a/src/core/models.py b/src/core/models.py index f3ba5e03ae..f7aaf8ce09 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -15,7 +15,11 @@ from bs4 import BeautifulSoup from django.conf import settings -from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +from django.contrib.auth.models import ( + AbstractBaseUser, + BaseUserManager, + PermissionsMixin, +) from django.core.exceptions import ValidationError from django.db import ( connection, @@ -63,9 +67,10 @@ {alt} """ + def profile_images_upload_path(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -74,91 +79,270 @@ def profile_images_upload_path(instance, filename): SALUTATION_CHOICES = ( - ('Miss', _('Miss')), - ('Ms', _('Ms')), - ('Mrs', _('Mrs')), - ('Mr', _('Mr')), - ('Mx', _('Mx')), - ('Dr', _('Dr')), - ('Prof.', _('Prof.')), + ("Miss", _("Miss")), + ("Ms", _("Ms")), + ("Mrs", _("Mrs")), + ("Mr", _("Mr")), + ("Mx", _("Mx")), + ("Dr", _("Dr")), + ("Prof.", _("Prof.")), ) -COUNTRY_CHOICES = [(u'AF', u'Afghanistan'), (u'AX', u'\xc5land Islands'), (u'AL', u'Albania'), - (u'DZ', u'Algeria'), (u'AS', u'American Samoa'), (u'AD', u'Andorra'), (u'AO', u'Angola'), - (u'AI', u'Anguilla'), (u'AQ', u'Antarctica'), (u'AG', u'Antigua and Barbuda'), (u'AR', u'Argentina'), - (u'AM', u'Armenia'), (u'AW', u'Aruba'), (u'AU', u'Australia'), (u'AT', u'Austria'), - (u'AZ', u'Azerbaijan'), (u'BS', u'Bahamas'), (u'BH', u'Bahrain'), (u'BD', u'Bangladesh'), - (u'BB', u'Barbados'), (u'BY', u'Belarus'), (u'BE', u'Belgium'), (u'BZ', u'Belize'), - (u'BJ', u'Benin'), (u'BM', u'Bermuda'), (u'BT', u'Bhutan'), - (u'BO', u'Bolivia, Plurinational State of'), (u'BQ', u'Bonaire, Sint Eustatius and Saba'), - (u'BA', u'Bosnia and Herzegovina'), (u'BW', u'Botswana'), (u'BV', u'Bouvet Island'), - (u'BR', u'Brazil'), (u'IO', u'British Indian Ocean Territory'), (u'BN', u'Brunei Darussalam'), - (u'BG', u'Bulgaria'), (u'BF', u'Burkina Faso'), (u'BI', u'Burundi'), (u'KH', u'Cambodia'), - (u'CM', u'Cameroon'), (u'CA', u'Canada'), (u'CV', u'Cape Verde'), (u'KY', u'Cayman Islands'), - (u'CF', u'Central African Republic'), (u'TD', u'Chad'), (u'CL', u'Chile'), (u'CN', u'China'), - (u'CX', u'Christmas Island'), (u'CC', u'Cocos (Keeling) Islands'), (u'CO', u'Colombia'), - (u'KM', u'Comoros'), (u'CG', u'Congo'), (u'CD', u'Congo, The Democratic Republic of the'), - (u'CK', u'Cook Islands'), (u'CR', u'Costa Rica'), (u'CI', u"C\xf4te d'Ivoire"), (u'HR', u'Croatia'), - (u'CU', u'Cuba'), (u'CW', u'Cura\xe7ao'), (u'CY', u'Cyprus'), (u'CZ', u'Czech Republic'), - (u'DK', u'Denmark'), (u'DJ', u'Djibouti'), (u'DM', u'Dominica'), (u'DO', u'Dominican Republic'), - (u'EC', u'Ecuador'), (u'EG', u'Egypt'), (u'SV', u'El Salvador'), (u'GQ', u'Equatorial Guinea'), - (u'ER', u'Eritrea'), (u'EE', u'Estonia'), (u'ET', u'Ethiopia'), - (u'FK', u'Falkland Islands (Malvinas)'), (u'FO', u'Faroe Islands'), (u'FJ', u'Fiji'), - (u'FI', u'Finland'), (u'FR', u'France'), (u'GF', u'French Guiana'), (u'PF', u'French Polynesia'), - (u'TF', u'French Southern Territories'), (u'GA', u'Gabon'), (u'GM', u'Gambia'), (u'GE', u'Georgia'), - (u'DE', u'Germany'), (u'GH', u'Ghana'), (u'GI', u'Gibraltar'), (u'GR', u'Greece'), - (u'GL', u'Greenland'), (u'GD', u'Grenada'), (u'GP', u'Guadeloupe'), (u'GU', u'Guam'), - (u'GT', u'Guatemala'), (u'GG', u'Guernsey'), (u'GN', u'Guinea'), (u'GW', u'Guinea-Bissau'), - (u'GY', u'Guyana'), (u'HT', u'Haiti'), (u'HM', u'Heard Island and McDonald Islands'), - (u'VA', u'Holy See (Vatican City State)'), (u'HN', u'Honduras'), (u'HK', u'Hong Kong'), - (u'HU', u'Hungary'), (u'IS', u'Iceland'), (u'IN', u'India'), (u'ID', u'Indonesia'), - (u'IR', u'Iran, Islamic Republic of'), (u'IQ', u'Iraq'), (u'IE', u'Ireland'), - (u'IM', u'Isle of Man'), (u'IL', u'Israel'), (u'IT', u'Italy'), (u'JM', u'Jamaica'), - (u'JP', u'Japan'), (u'JE', u'Jersey'), (u'JO', u'Jordan'), (u'KZ', u'Kazakhstan'), (u'KE', u'Kenya'), - (u'KI', u'Kiribati'), (u'KP', u"Korea, Democratic People's Republic of"), - (u'KR', u'Korea, Republic of'), (u'KW', u'Kuwait'), (u'KG', u'Kyrgyzstan'), - (u'LA', u"Lao People's Democratic Republic"), (u'LV', u'Latvia'), (u'LB', u'Lebanon'), - (u'LS', u'Lesotho'), (u'LR', u'Liberia'), (u'LY', u'Libya'), (u'LI', u'Liechtenstein'), - (u'LT', u'Lithuania'), (u'LU', u'Luxembourg'), (u'MO', u'Macao'), (u'MK', u'Macedonia, Republic of'), - (u'MG', u'Madagascar'), (u'MW', u'Malawi'), (u'MY', u'Malaysia'), (u'MV', u'Maldives'), - (u'ML', u'Mali'), (u'MT', u'Malta'), (u'MH', u'Marshall Islands'), (u'MQ', u'Martinique'), - (u'MR', u'Mauritania'), (u'MU', u'Mauritius'), (u'YT', u'Mayotte'), (u'MX', u'Mexico'), - (u'FM', u'Micronesia, Federated States of'), (u'MD', u'Moldova, Republic of'), (u'MC', u'Monaco'), - (u'MN', u'Mongolia'), (u'ME', u'Montenegro'), (u'MS', u'Montserrat'), (u'MA', u'Morocco'), - (u'MZ', u'Mozambique'), (u'MM', u'Myanmar'), (u'NA', u'Namibia'), (u'NR', u'Nauru'), - (u'NP', u'Nepal'), (u'NL', u'Netherlands'), (u'NC', u'New Caledonia'), (u'NZ', u'New Zealand'), - (u'NI', u'Nicaragua'), (u'NE', u'Niger'), (u'NG', u'Nigeria'), (u'NU', u'Niue'), - (u'NF', u'Norfolk Island'), (u'MP', u'Northern Mariana Islands'), (u'NO', u'Norway'), - (u'OM', u'Oman'), (u'PK', u'Pakistan'), (u'PW', u'Palau'), (u'PS', u'Palestine, State of'), - (u'PA', u'Panama'), (u'PG', u'Papua New Guinea'), (u'PY', u'Paraguay'), (u'PE', u'Peru'), - (u'PH', u'Philippines'), (u'PN', u'Pitcairn'), (u'PL', u'Poland'), (u'PT', u'Portugal'), - (u'PR', u'Puerto Rico'), (u'QA', u'Qatar'), (u'RE', u'R\xe9union'), (u'RO', u'Romania'), - (u'RU', u'Russian Federation'), (u'RW', u'Rwanda'), (u'BL', u'Saint Barth\xe9lemy'), - (u'SH', u'Saint Helena, Ascension and Tristan da Cunha'), (u'KN', u'Saint Kitts and Nevis'), - (u'LC', u'Saint Lucia'), (u'MF', u'Saint Martin (French part)'), - (u'PM', u'Saint Pierre and Miquelon'), (u'VC', u'Saint Vincent and the Grenadines'), - (u'WS', u'Samoa'), (u'SM', u'San Marino'), (u'ST', u'Sao Tome and Principe'), - (u'SA', u'Saudi Arabia'), (u'SN', u'Senegal'), (u'RS', u'Serbia'), (u'SC', u'Seychelles'), - (u'SL', u'Sierra Leone'), (u'SG', u'Singapore'), (u'SX', u'Sint Maarten (Dutch part)'), - (u'SK', u'Slovakia'), (u'SI', u'Slovenia'), (u'SB', u'Solomon Islands'), (u'SO', u'Somalia'), - (u'ZA', u'South Africa'), (u'GS', u'South Georgia and the South Sandwich Islands'), - (u'ES', u'Spain'), (u'LK', u'Sri Lanka'), (u'SD', u'Sudan'), (u'SR', u'Suriname'), - (u'SS', u'South Sudan'), (u'SJ', u'Svalbard and Jan Mayen'), (u'SZ', u'Swaziland'), - (u'SE', u'Sweden'), (u'CH', u'Switzerland'), (u'SY', u'Syrian Arab Republic'), - (u'TW', u'Taiwan, Province of China'), (u'TJ', u'Tajikistan'), - (u'TZ', u'Tanzania, United Republic of'), (u'TH', u'Thailand'), (u'TL', u'Timor-Leste'), - (u'TG', u'Togo'), (u'TK', u'Tokelau'), (u'TO', u'Tonga'), (u'TT', u'Trinidad and Tobago'), - (u'TN', u'Tunisia'), (u'TR', u'Turkey'), (u'TM', u'Turkmenistan'), - (u'TC', u'Turks and Caicos Islands'), (u'TV', u'Tuvalu'), (u'UG', u'Uganda'), (u'UA', u'Ukraine'), - (u'AE', u'United Arab Emirates'), (u'GB', u'United Kingdom'), (u'US', u'United States'), - (u'UM', u'United States Minor Outlying Islands'), (u'UY', u'Uruguay'), (u'UZ', u'Uzbekistan'), - (u'VU', u'Vanuatu'), (u'VE', u'Venezuela, Bolivarian Republic of'), (u'VN', u'Viet Nam'), - (u'VG', u'Virgin Islands, British'), (u'VI', u'Virgin Islands, U.S.'), (u'WF', u'Wallis and Futuna'), - (u'EH', u'Western Sahara'), (u'YE', u'Yemen'), (u'ZM', u'Zambia'), (u'ZW', u'Zimbabwe')] +COUNTRY_CHOICES = [ + ("AF", "Afghanistan"), + ("AX", "\xc5land Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia, Plurinational State of"), + ("BQ", "Bonaire, Sint Eustatius and Saba"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei Darussalam"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("CV", "Cape Verde"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo, The Democratic Republic of the"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "C\xf4te d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CW", "Cura\xe7ao"), + ("CY", "Cyprus"), + ("CZ", "Czech Republic"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See (Vatican City State)"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran, Islamic Republic of"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KP", "Korea, Democratic People's Republic of"), + ("KR", "Korea, Republic of"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Lao People's Democratic Republic"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MK", "Macedonia, Republic of"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia, Federated States of"), + ("MD", "Moldova, Republic of"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestine, State of"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "R\xe9union"), + ("RO", "Romania"), + ("RU", "Russian Federation"), + ("RW", "Rwanda"), + ("BL", "Saint Barth\xe9lemy"), + ("SH", "Saint Helena, Ascension and Tristan da Cunha"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin (French part)"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SX", "Sint Maarten (Dutch part)"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SS", "South Sudan"), + ("SJ", "Svalbard and Jan Mayen"), + ("SZ", "Swaziland"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syrian Arab Republic"), + ("TW", "Taiwan, Province of China"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania, United Republic of"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("GB", "United Kingdom"), + ("US", "United States"), + ("UM", "United States Minor Outlying Islands"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela, Bolivarian Republic of"), + ("VN", "Viet Nam"), + ("VG", "Virgin Islands, British"), + ("VI", "Virgin Islands, U.S."), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), +] TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones)) -SUMMERNOTE_SENTINEL = '


' +SUMMERNOTE_SENTINEL = "


" class Country(models.Model): @@ -166,8 +350,8 @@ class Country(models.Model): name = models.TextField(max_length=255) class Meta: - ordering = ('name', 'code') - verbose_name_plural = 'countries' + ordering = ("name", "code") + verbose_name_plural = "countries" def __str__(self): return self.name @@ -184,7 +368,7 @@ def create(self, **kwargs): class AccountManager(BaseUserManager): def create_user(self, username=None, password=None, email=None, **kwargs): - """ Creates a user from the given username or email + """Creates a user from the given username or email In Janeway, users rely on email addresses to log in. For compatibility with 3rd party libraries, we allow a username argument, however only a email address will be accepted as the username and email. @@ -196,8 +380,8 @@ def create_user(self, username=None, password=None, email=None, **kwargs): try: validate_email(email) email = self.normalize_email(email) - except(ValidationError, TypeError, ValueError): - raise ValueError(f'{email} not a valid email address.') + except (ValidationError, TypeError, ValueError): + raise ValueError(f"{email} not a valid email address.") account = self.model( # The original case of the email is preserved @@ -232,26 +416,26 @@ def get_queryset(self): class Account(AbstractBaseUser, PermissionsMixin): - email = PGCaseInsensitiveEmailField(unique=True, verbose_name=_('Email')) - username = models.CharField(max_length=254, unique=True, verbose_name=_('Username')) + email = PGCaseInsensitiveEmailField(unique=True, verbose_name=_("Email")) + username = models.CharField(max_length=254, unique=True, verbose_name=_("Username")) name_prefix = models.CharField(max_length=10, blank=True) first_name = models.CharField( max_length=300, blank=False, - verbose_name=_('First name'), + verbose_name=_("First name"), validators=[plain_text_validator], ) middle_name = models.CharField( max_length=300, blank=True, - verbose_name=_('Middle name'), + verbose_name=_("Middle name"), validators=[plain_text_validator], ) last_name = models.CharField( max_length=300, blank=False, - verbose_name=_('Last name'), + verbose_name=_("Last name"), validators=[plain_text_validator], ) @@ -260,59 +444,81 @@ class Account(AbstractBaseUser, PermissionsMixin): max_length=10, choices=SALUTATION_CHOICES, blank=True, - verbose_name=_('Salutation'), + verbose_name=_("Salutation"), validators=[plain_text_validator], ) suffix = models.CharField( max_length=300, blank=True, - verbose_name=_('Name suffix'), + verbose_name=_("Name suffix"), validators=[plain_text_validator], ) biography = JanewayBleachField( blank=True, - verbose_name=_('Biography'), + verbose_name=_("Biography"), + ) + orcid = models.CharField( + max_length=40, null=True, blank=True, verbose_name=_("ORCiD") ) - orcid = models.CharField(max_length=40, null=True, blank=True, verbose_name=_('ORCiD')) institution = models.CharField( max_length=1000, blank=True, - verbose_name=_('Institution'), + verbose_name=_("Institution"), validators=[plain_text_validator], ) department = models.CharField( max_length=300, blank=True, - verbose_name=_('Department'), + verbose_name=_("Department"), validators=[plain_text_validator], ) - twitter = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Twitter Handle')) - facebook = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Facebook Handle')) - linkedin = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Linkedin Profile')) - website = models.URLField(max_length=300, null=True, blank=True, verbose_name=_('Website')) - github = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Github Username')) - profile_image = models.ImageField(upload_to=profile_images_upload_path, null=True, blank=True, storage=fs, verbose_name=("Profile Image")) + twitter = models.CharField( + max_length=300, null=True, blank=True, verbose_name=_("Twitter Handle") + ) + facebook = models.CharField( + max_length=300, null=True, blank=True, verbose_name=_("Facebook Handle") + ) + linkedin = models.CharField( + max_length=300, null=True, blank=True, verbose_name=_("Linkedin Profile") + ) + website = models.URLField( + max_length=300, null=True, blank=True, verbose_name=_("Website") + ) + github = models.CharField( + max_length=300, null=True, blank=True, verbose_name=_("Github Username") + ) + profile_image = models.ImageField( + upload_to=profile_images_upload_path, + null=True, + blank=True, + storage=fs, + verbose_name=("Profile Image"), + ) email_sent = models.DateTimeField(blank=True, null=True) date_confirmed = models.DateTimeField(blank=True, null=True) - confirmation_code = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Confirmation Code")) + confirmation_code = models.CharField( + max_length=200, blank=True, null=True, verbose_name=_("Confirmation Code") + ) signature = JanewayBleachField( blank=True, verbose_name=_("Signature"), ) - interest = models.ManyToManyField('Interest', null=True, blank=True) + interest = models.ManyToManyField("Interest", null=True, blank=True) country = models.ForeignKey( Country, null=True, blank=True, - verbose_name=_('Country'), + verbose_name=_("Country"), on_delete=models.SET_NULL, ) preferred_timezone = DynamicChoiceField( - max_length=300, null=True, blank=True, - choices=tuple(), - dynamic_choices=TIMEZONE_CHOICES, - verbose_name=_("Preferred Timezone") - ) + max_length=300, + null=True, + blank=True, + choices=tuple(), + dynamic_choices=TIMEZONE_CHOICES, + verbose_name=_("Preferred Timezone"), + ) is_active = models.BooleanField(default=False) is_staff = models.BooleanField(default=False) @@ -324,9 +530,7 @@ class Account(AbstractBaseUser, PermissionsMixin): ) enable_public_profile = models.BooleanField( default=False, - help_text=_( - 'If enabled, your basic profile will be available to the public.' - ), + help_text=_("If enabled, your basic profile will be available to the public."), verbose_name=_("Enable public profile"), ) @@ -336,14 +540,14 @@ class Account(AbstractBaseUser, PermissionsMixin): objects = AccountManager() - USERNAME_FIELD = 'email' + USERNAME_FIELD = "email" class Meta: - ordering = ('first_name', 'last_name', 'username') - unique_together = ('email', 'username') + ordering = ("first_name", "last_name", "username") + unique_together = ("email", "username") def clean(self, *args, **kwargs): - """ Normalizes the email address + """Normalizes the email address The username is lowercased instead, to cope with a bug present for accounts imported/registered prior to v.1.3.8 @@ -360,15 +564,13 @@ def __str__(self): return self.full_name() def password_policy_check(self, request, password): - rules = [ - lambda s: len(password) >= request.press.password_length or 'length' - ] + rules = [lambda s: len(password) >= request.press.password_length or "length"] if request.press.password_upper: - rules.append(lambda password: any(x.isupper() for x in password) or 'upper') + rules.append(lambda password: any(x.isupper() for x in password) or "upper") if request.press.password_number: - rules.append(lambda password: any(x.isdigit() for x in password) or 'digit') + rules.append(lambda password: any(x.isdigit() for x in password) or "digit") problems = [p for p in [r(password) for r in rules] if p != True] @@ -386,7 +588,7 @@ def get_short_name(self): @property def first_names(self): - return ' '.join([self.first_name, self.middle_name]) + return " ".join([self.first_name, self.middle_name]) def full_name(self): name_elements = [ @@ -400,18 +602,22 @@ def full_name(self): def salutation_name(self): if self.salutation: - return u"%s %s" % (self.salutation, self.last_name) + return "%s %s" % (self.salutation, self.last_name) else: - return u"%s %s" % (self.first_name, self.last_name) + return "%s %s" % (self.first_name, self.last_name) def initials(self): if self.first_name and self.last_name: if self.middle_name: - return u"%s%s%s" % (self.first_name[:1], self.middle_name[:1], self.last_name[:1]) + return "%s%s%s" % ( + self.first_name[:1], + self.middle_name[:1], + self.last_name[:1], + ) else: - return u"%s%s" % (self.first_name[:1], self.last_name[:1]) + return "%s%s" % (self.first_name[:1], self.last_name[:1]) else: - return 'N/A' + return "N/A" def affiliation(self): if self.institution and self.department: @@ -419,7 +625,7 @@ def affiliation(self): elif self.institution: return self.institution else: - return '' + return "" def active_reviews(self): return review_models.ReviewAssignment.objects.filter( @@ -428,7 +634,9 @@ def active_reviews(self): ) def active_copyedits(self): - return copyediting_models.CopyeditAssignment.objects.filter(copyeditor=self, copyedit_acknowledged=False) + return copyediting_models.CopyeditAssignment.objects.filter( + copyeditor=self, copyedit_acknowledged=False + ) def active_typesets(self): """ @@ -437,9 +645,14 @@ def active_typesets(self): """ from production import models as production_models from proofing import models as proofing_models + task_list = list() - typeset_tasks = production_models.TypesetTask.objects.filter(typesetter=self, completed__isnull=True) - proofing_tasks = proofing_models.TypesetterProofingTask.objects.filter(typesetter=self, completed__isnull=True) + typeset_tasks = production_models.TypesetTask.objects.filter( + typesetter=self, completed__isnull=True + ) + proofing_tasks = proofing_models.TypesetterProofingTask.objects.filter( + typesetter=self, completed__isnull=True + ) for task in typeset_tasks: task_list.append(task) @@ -470,8 +683,8 @@ def roles(self): def roles_for_journal(self, journal): return [ - account_role.role for account_role in - AccountRole.objects.filter(user=self, journal=journal) + account_role.role + for account_role in AccountRole.objects.filter(user=self, journal=journal) ] def check_role(self, journal, role, staff_override=True): @@ -487,19 +700,19 @@ def check_role(self, journal, role, staff_override=True): def is_journal_manager(self, journal): # this is an explicit check to avoid recursion in check_role. return AccountRole.objects.filter( - user=self, - journal=journal, - role__slug='journal-manager', - ).exists() + user=self, + journal=journal, + role__slug="journal-manager", + ).exists() def is_editor(self, request, journal=None): if not journal: - return self.check_role(request.journal, 'editor') + return self.check_role(request.journal, "editor") else: - return self.check_role(journal, 'editor') + return self.check_role(journal, "editor") def is_section_editor(self, request): - return self.check_role(request.journal, 'section-editor') + return self.check_role(request.journal, "section-editor") def has_an_editor_role(self, request): editor = self.is_editor(request) @@ -511,25 +724,25 @@ def has_an_editor_role(self, request): return False def is_reviewer(self, request): - return self.check_role(request.journal, 'reviewer') + return self.check_role(request.journal, "reviewer") def is_author(self, request): - return self.check_role(request.journal, 'author') + return self.check_role(request.journal, "author") def is_proofreader(self, request): - return self.check_role(request.journal, 'proofreader') + return self.check_role(request.journal, "proofreader") def is_production(self, request): - return self.check_role(request.journal, 'production') + return self.check_role(request.journal, "production") def is_copyeditor(self, request): - return self.check_role(request.journal, 'copyeditor') + return self.check_role(request.journal, "copyeditor") def is_typesetter(self, request): - return self.check_role(request.journal, 'typesetter') + return self.check_role(request.journal, "typesetter") def is_proofing_manager(self, request): - return self.check_role(request.journal, 'proofing-manager') + return self.check_role(request.journal, "proofing-manager") def is_repository_manager(self, repository): if self in repository.managers.all(): @@ -545,14 +758,14 @@ def is_preprint_editor(self, request): def snapshot_self(self, article, force_update=True): frozen_dict = { - 'name_prefix': self.name_prefix, - 'first_name': self.first_name, - 'middle_name': self.middle_name, - 'last_name': self.last_name, - 'name_suffix': self.suffix, - 'institution': self.institution, - 'department': self.department, - 'display_email': True if self == article.correspondence_author else False, + "name_prefix": self.name_prefix, + "first_name": self.first_name, + "middle_name": self.middle_name, + "last_name": self.last_name, + "name_suffix": self.suffix, + "institution": self.institution, + "department": self.department, + "display_email": True if self == article.correspondence_author else False, } frozen_author = self.frozen_author(article) @@ -567,16 +780,16 @@ def snapshot_self(self, article, force_update=True): order_object = article.articleauthororder_set.get(author=self) except submission_models.ArticleAuthorOrder.DoesNotExist: order_integer = article.next_author_sort() - order_object, c = submission_models.ArticleAuthorOrder.objects.get_or_create( - article=article, - author=self, - defaults={'order': order_integer} + order_object, c = ( + submission_models.ArticleAuthorOrder.objects.get_or_create( + article=article, author=self, defaults={"order": order_integer} + ) ) submission_models.FrozenAuthor.objects.get_or_create( author=self, article=article, - defaults=dict(order=order_object.order, **frozen_dict) + defaults=dict(order=order_object.order, **frozen_dict), ) def frozen_author(self, article): @@ -590,7 +803,9 @@ def frozen_author(self, article): @property def average_reviewer_score(self): - reviewer_ratings = review_models.ReviewerRating.objects.filter(assignment__reviewer=self) + reviewer_ratings = review_models.ReviewerRating.objects.filter( + assignment__reviewer=self + ) ratings = [reviewer_rating.rating for reviewer_rating in reviewer_ratings] return statistics.mean(ratings) if ratings else 0 @@ -613,6 +828,7 @@ def published_articles(self): def preprint_subjects(self): "Returns a list of preprint subjects this user is an editor for" from repository import models as repository_models + subjects = repository_models.Subject.objects.filter( editors__exact=self, ) @@ -620,9 +836,9 @@ def preprint_subjects(self): @property def hypothesis_username(self): - username = '{pk}{first_name}{last_name}'.format(pk=self.pk, - first_name=self.first_name, - last_name=self.last_name)[:30] + username = "{pk}{first_name}{last_name}".format( + pk=self.pk, first_name=self.first_name, last_name=self.last_name + )[:30] return username.lower() @@ -633,7 +849,9 @@ def generate_expiry_date(): class OrcidToken(models.Model): token = models.UUIDField(default=uuid.uuid4) orcid = models.CharField(max_length=200) - expiry = models.DateTimeField(default=generate_expiry_date, verbose_name=_('Expires on')) + expiry = models.DateTimeField( + default=generate_expiry_date, verbose_name=_("Expires on") + ) def __str__(self): return "ORCiD Token [{0}] - {1}".format(self.orcid, self.token) @@ -645,14 +863,16 @@ class PasswordResetToken(models.Model): on_delete=models.CASCADE, ) token = models.CharField(max_length=300, default=uuid.uuid4) - expiry = models.DateTimeField(default=generate_expiry_date, verbose_name=_('Expires on')) + expiry = models.DateTimeField( + default=generate_expiry_date, verbose_name=_("Expires on") + ) expired = models.BooleanField(default=False) def __str__(self): return "Account: {0}, Expiry: {1}, [{2}]".format( self.account.full_name(), self.expiry, - 'Expired' if self.expired else 'Active', + "Expired" if self.expired else "Active", ) def has_expired(self): @@ -664,29 +884,29 @@ def has_expired(self): return False class Meta: - ordering = ['-expiry'] + ordering = ["-expiry"] class Role(models.Model): name = models.CharField( max_length=100, - help_text='Display name for this role ' - '(can include spaces and capital letters)', + help_text="Display name for this role " + "(can include spaces and capital letters)", ) slug = models.CharField( max_length=100, - help_text='Normalized string representing this role ' - 'containing only lowercase letters and hyphens.', + help_text="Normalized string representing this role " + "containing only lowercase letters and hyphens.", ) class Meta: - ordering = ('name', 'slug') + ordering = ("name", "slug") def __str__(self): - return u'%s' % self.name + return "%s" % self.name def __repr__(self): - return u'%s' % self.name + return "%s" % self.name class AccountRole(models.Model): @@ -695,7 +915,7 @@ class AccountRole(models.Model): on_delete=models.CASCADE, ) journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) role = models.ForeignKey( @@ -704,8 +924,8 @@ class AccountRole(models.Model): ) class Meta: - unique_together = ('journal', 'user', 'role') - ordering = ('journal', 'role') + unique_together = ("journal", "user", "role") + ordering = ("journal", "role") def __str__(self): return "{0} {1} {2}".format(self.user, self.journal, self.role.name) @@ -715,31 +935,31 @@ class Interest(models.Model): name = models.CharField(max_length=250) def __str__(self): - return u'%s' % self.name + return "%s" % self.name def __repr__(self): - return u'%s' % self.name + return "%s" % self.name setting_types = ( - ('rich-text', 'Rich Text'), - ('mini-html', 'Mini HTML'), - ('text', 'Plain Text'), - ('char', 'Characters'), - ('number', 'Number'), - ('boolean', 'Boolean'), - ('file', 'File'), - ('select', 'Select'), - ('json', 'JSON'), + ("rich-text", "Rich Text"), + ("mini-html", "Mini HTML"), + ("text", "Plain Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), ) privacy_types = ( - ('public', 'Public'), - ('typesetters', 'Typesetters'), - ('proofreaders', 'Proofreaders'), - ('copyeditors', 'Copyedtiors'), - ('editors', 'Editors'), - ('owner', 'Owner'), + ("public", "Public"), + ("typesetters", "Typesetters"), + ("proofreaders", "Proofreaders"), + ("copyeditors", "Copyedtiors"), + ("editors", "Editors"), + ("owner", "Owner"), ) @@ -751,10 +971,10 @@ class SettingGroup(models.Model): enabled = models.BooleanField(default=True) def __str__(self): - return u'%s' % self.name + return "%s" % self.name def __repr__(self): - return u'%s' % self.name + return "%s" % self.name def validate(self, value): if self.name in self.VALIDATORS: @@ -770,7 +990,7 @@ class Setting(models.Model): on_delete=models.CASCADE, ) types = models.CharField(max_length=20, choices=setting_types) - pretty_name = models.CharField(max_length=100, default='') + pretty_name = models.CharField(max_length=100, default="") description = models.TextField(null=True, blank=True) is_translatable = models.BooleanField(default=False) @@ -778,24 +998,24 @@ class Setting(models.Model): editable_by = models.ManyToManyField( Role, blank=True, - help_text='Determines who can edit this setting based on their assigned roles.', + help_text="Determines who can edit this setting based on their assigned roles.", ) class Meta: - ordering = ('group', 'name') + ordering = ("group", "name") def __str__(self): - return u'%s' % self.name + return "%s" % self.name def __repr__(self): - return u'%s' % self.name + return "%s" % self.name @property def default_setting_value(self): return SettingValue.objects.get( setting=self, journal=None, - ) + ) def validate(self, value): if self.types in self.VALIDATORS: @@ -807,7 +1027,7 @@ def validate(self, value): class SettingValue(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", null=True, blank=True, on_delete=models.CASCADE, @@ -819,9 +1039,7 @@ class SettingValue(models.Model): value = models.TextField(null=True, blank=True) class Meta: - unique_together = ( - ("journal", "setting"), - ) + unique_together = (("journal", "setting"),) def __repr__(self): if self.journal: @@ -838,53 +1056,51 @@ def processed_value(self): return self.process_value() def process_value(self): - """ Converts string values of settings to proper values + """Converts string values of settings to proper values :return: a value """ - if self.setting.types == 'boolean' and self.value == 'on': + if self.setting.types == "boolean" and self.value == "on": return True - elif self.setting.types == 'boolean': + elif self.setting.types == "boolean": return False - elif self.setting.types == 'number': + elif self.setting.types == "number": try: return int(self.value) except BaseException: return 0 - elif self.setting.types == 'json' and self.value: + elif self.setting.types == "json" and self.value: try: return json.loads(self.value) except json.JSONDecodeError as e: logger.error( "Error loading JSON setting {setting_name} on {site_name} site.".format( setting_name=self.setting.name, - site_name=self.journal.name if self.journal else 'press' + site_name=self.journal.name if self.journal else "press", ) ) - return '' - elif self.setting.types == 'rich-text' and self.value == SUMMERNOTE_SENTINEL: - return '' + return "" + elif self.setting.types == "rich-text" and self.value == SUMMERNOTE_SENTINEL: + return "" else: return self.value @property def render_value(self): - """ Converts string values of settings to values for rendering + """Converts string values of settings to values for rendering :return: a value """ - if self.setting.types == 'boolean' and not self.value: + if self.setting.types == "boolean" and not self.value: return "off" - elif self.setting.types == 'boolean': + elif self.setting.types == "boolean": return "on" - elif self.setting.types == 'file': + elif self.setting.types == "file": if self.journal: - return self.journal.site_url( - reverse("journal_file", self.value)) + return self.journal.site_url(reverse("journal_file", self.value)) else: - return self.press.site_url( - reverse("serve_press_file", self.value)) + return self.press.site_url(reverse("serve_press_file", self.value)) else: return self.value @@ -894,6 +1110,7 @@ def press(self): return self.journal.press else: from press.models import Press + return Press.objects.all()[0] def validate(self): @@ -909,13 +1126,19 @@ def save(self, *args, **kwargs): class File(AbstractLastModifiedModel): - article_id = models.PositiveIntegerField(blank=True, null=True, verbose_name=_('Article PK')) + article_id = models.PositiveIntegerField( + blank=True, null=True, verbose_name=_("Article PK") + ) mime_type = models.CharField(max_length=255) original_filename = models.CharField(max_length=1000) uuid_filename = models.CharField(max_length=100) - label = models.CharField(max_length=1000, null=True, blank=True, verbose_name=_('Label')) - description = JanewayBleachField(null=True, blank=True, verbose_name=_('Description')) + label = models.CharField( + max_length=1000, null=True, blank=True, verbose_name=_("Label") + ) + description = JanewayBleachField( + null=True, blank=True, verbose_name=_("Description") + ) sequence = models.IntegerField(default=1) owner = models.ForeignKey(Account, null=True, on_delete=models.SET_NULL) privacy = models.CharField(max_length=20, choices=privacy_types, default="owner") @@ -926,20 +1149,24 @@ class File(AbstractLastModifiedModel): # Remote galley handling is_remote = models.BooleanField(default=False) - remote_url = models.URLField(blank=True, null=True, verbose_name=_('Remote URL of file')) + remote_url = models.URLField( + blank=True, null=True, verbose_name=_("Remote URL of file") + ) history = models.ManyToManyField( - 'FileHistory', + "FileHistory", blank=True, ) - text = models.OneToOneField(swapper.get_model_name('core', 'FileText'), - blank=True, null=True, + text = models.OneToOneField( + swapper.get_model_name("core", "FileText"), + blank=True, + null=True, related_name="file", on_delete=models.SET_NULL, ) class Meta: - ordering = ('sequence', 'pk') + ordering = ("sequence", "pk") def save(self, *args, **kwargs): super().save(*args, **kwargs) @@ -966,7 +1193,7 @@ def unlink_file(self, journal=None): path = self.self_article_path() os.unlink(path) except FileNotFoundError: - print('file_not_found') + print("file_not_found") elif journal: try: path = self.journal_path(journal) @@ -979,22 +1206,39 @@ def unlink_preprint_file(self): os.unlink(path) def preprint_path(self): - return os.path.join(settings.BASE_DIR, 'files', 'press', 'preprints', str(self.uuid_filename)) + return os.path.join( + settings.BASE_DIR, "files", "press", "preprints", str(self.uuid_filename) + ) def press_path(self): - return os.path.join(settings.BASE_DIR, 'files', 'press', str(self.uuid_filename)) + return os.path.join( + settings.BASE_DIR, "files", "press", str(self.uuid_filename) + ) def journal_path(self, journal): - return os.path.join(settings.BASE_DIR, 'files', 'journals', str(journal.pk), str(self.uuid_filename)) + return os.path.join( + settings.BASE_DIR, + "files", + "journals", + str(journal.pk), + str(self.uuid_filename), + ) def self_article_path(self): if self.article_id: - return os.path.join(settings.BASE_DIR, 'files', 'articles', str(self.article_id), str(self.uuid_filename)) + return os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(self.article_id), + str(self.uuid_filename), + ) def url(self): from core.middleware import GlobalRequestMiddleware + request = GlobalRequestMiddleware.get_current_request() - url_kwargs = {'file_id': self.pk} + url_kwargs = {"file_id": self.pk} if request.journal and self.article_id: raise NotImplementedError @@ -1002,7 +1246,7 @@ def url(self): raise NotImplementedError else: return reverse( - 'serve_press_file', + "serve_press_file", kwargs=url_kwargs, ) @@ -1010,11 +1254,24 @@ def get_file(self, article, as_bytes=False): return files.get_file(self, article, as_bytes=as_bytes) def get_file_path(self, article): - return os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id), str(self.uuid_filename)) + return os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(article.id), + str(self.uuid_filename), + ) def get_file_size(self, article): - return os.path.getsize(os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id), - str(self.uuid_filename))) + return os.path.getsize( + os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(article.id), + str(self.uuid_filename), + ) + ) def next_history_seq(self): try: @@ -1028,14 +1285,15 @@ def checksum(self): try: return files.checksum(self.self_article_path()) except FileNotFoundError: - return 'No checksum could be calculated.' + return "No checksum could be calculated." else: logger.error( - 'Galley file ({file_id}) found with no article_id.'.format( + "Galley file ({file_id}) found with no article_id.".format( file_id=self.pk - ), extra={'stack': True} + ), + extra={"stack": True}, ) - return 'No checksum could be calculated.' + return "No checksum could be calculated." def public_download_name(self): article = self.article @@ -1053,24 +1311,22 @@ def public_download_name(self): ) else: logger.warning( - 'Article {pk} has no author records'.format( - pk=article.pk - ) + "Article {pk} has no author records".format(pk=article.pk) ) - author_surname = '' + author_surname = "" - file_name = '{code}-{pk}{surname}{extension}'.format( + file_name = "{code}-{pk}{surname}{extension}".format( code=article.journal.code, pk=article.pk, surname=author_surname, - extension=extension + extension=extension, ) return file_name.lower() else: return self.original_filename def index_full_text(self, save=True): - """ Extracts text from the File and stores it into an indexed model + """Extracts text from the File and stores it into an indexed model Depending on the database backend, preprocessing ahead of indexing varies; As an example, for Postgresql, instead of storing the text as a LOB, a @@ -1114,14 +1370,16 @@ def index_full_text(self, save=True): @property def date_modified(self): - warnings.warn("'date_modified' is deprecated and will be removed, use last_modified instead.") + warnings.warn( + "'date_modified' is deprecated and will be removed, use last_modified instead." + ) return self.last_modified def __str__(self): - return u'%s' % self.original_filename + return "%s" % self.original_filename def __repr__(self): - return u'%s' % self.original_filename + return "%s" % self.original_filename class AbstractFileText(models.Model): @@ -1146,7 +1404,8 @@ def save(self, *args, **kwargs): class FileText(AbstractFileText): class Meta: - swappable = swapper.swappable_setting('core', 'FileText') + swappable = swapper.swappable_setting("core", "FileText") + pass @@ -1156,10 +1415,9 @@ class PGFileText(AbstractFileText): class Meta: required_db_vendor = "postgresql" - @staticmethod def preprocess_contents(text, lang=None): - """ Casts the given text into a postgres TSVector + """Casts the given text into a postgres TSVector The result can be cached so that there is no need to store the original text and also speed up the search queries. @@ -1171,14 +1429,14 @@ def preprocess_contents(text, lang=None): """ cursor = connection.cursor() # Remove NULL characters before vectorising - text = text.replace("\x00", "\uFFFD") + text = text.replace("\x00", "\ufffd") result = cursor.execute("SELECT to_tsvector(%s) as vector", [text]) return cursor.fetchone()[0] @receiver(models.signals.pre_save, sender=File) def update_file_index(sender, instance, **kwargs): - """ Updates the indexed contents in the database """ + """Updates the indexed contents in the database""" if not instance.pk: return False @@ -1187,13 +1445,19 @@ def update_file_index(sender, instance, **kwargs): class FileHistory(models.Model): - article_id = models.PositiveIntegerField(blank=True, null=True, verbose_name=_('Article PK')) + article_id = models.PositiveIntegerField( + blank=True, null=True, verbose_name=_("Article PK") + ) mime_type = models.CharField(max_length=255) original_filename = models.CharField(max_length=1000) uuid_filename = models.CharField(max_length=100) - label = models.CharField(max_length=200, null=True, blank=True, verbose_name=_('Label')) - description = JanewayBleachField(null=True, blank=True, verbose_name=_('Description')) + label = models.CharField( + max_length=200, null=True, blank=True, verbose_name=_("Label") + ) + description = JanewayBleachField( + null=True, blank=True, verbose_name=_("Description") + ) sequence = models.IntegerField(default=1) owner = models.ForeignKey(Account, null=True, on_delete=models.SET_NULL) privacy = models.CharField(max_length=20, choices=privacy_types, default="owner") @@ -1204,40 +1468,48 @@ def __str__(self): return "Iteration {0}: {1}".format(self.history_seq, self.original_filename) class Meta: - ordering = ('history_seq',) - verbose_name_plural = 'file histories' + ordering = ("history_seq",) + verbose_name_plural = "file histories" def galley_type_choices(): return ( - ('pdf', 'PDF'), - ('epub', 'EPUB'), - ('html', 'HTML'), - ('xml', 'XML'), - ('doc', 'Word (Doc)'), - ('docx', 'Word (DOCX)'), - ('odt', 'OpenDocument Text Document'), - ('tex', 'LaTeX'), - ('rtf', 'RTF'), - ('other', _('Other')), - ('image', _('Image')), + ("pdf", "PDF"), + ("epub", "EPUB"), + ("html", "HTML"), + ("xml", "XML"), + ("doc", "Word (Doc)"), + ("docx", "Word (DOCX)"), + ("odt", "OpenDocument Text Document"), + ("tex", "LaTeX"), + ("rtf", "RTF"), + ("other", _("Other")), + ("image", _("Image")), ) class Galley(AbstractLastModifiedModel): # Local Galley article = models.ForeignKey( - 'submission.Article', + "submission.Article", null=True, on_delete=models.CASCADE, ) file = models.ForeignKey(File, on_delete=models.CASCADE) - css_file = models.ForeignKey(File, related_name='css_file', null=True, blank=True, on_delete=models.SET_NULL) - images = models.ManyToManyField(File, related_name='images', null=True, blank=True) - xsl_file = models.ForeignKey('core.XSLFile', related_name='xsl_file', null=True, blank=True, on_delete=models.SET_NULL) + css_file = models.ForeignKey( + File, related_name="css_file", null=True, blank=True, on_delete=models.SET_NULL + ) + images = models.ManyToManyField(File, related_name="images", null=True, blank=True) + xsl_file = models.ForeignKey( + "core.XSLFile", + related_name="xsl_file", + null=True, + blank=True, + on_delete=models.SET_NULL, + ) public = models.BooleanField( default=True, - help_text='Uncheck if the typeset file should not be publicly available after the article is published.' + help_text="Uncheck if the typeset file should not be publicly available after the article is published.", ) # Remote Galley @@ -1248,8 +1520,8 @@ class Galley(AbstractLastModifiedModel): label = models.CharField( max_length=400, help_text='Typeset file labels are displayed in download links and have the format "Download Label" eg. if ' - 'you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for ' - 'you, leave it blank.', + "you set the label to be PDF the link will be Download PDF. If you want Janeway to set a label for " + "you, leave it blank.", ) type = models.CharField(max_length=100, choices=galley_type_choices()) sequence = models.IntegerField(default=0) @@ -1258,7 +1530,7 @@ def unlink_files(self): if self.file and self.file.article_id: self.file.unlink_file() for image_file in self.images.all(): - if not image_file.images.exclude(galley=self).exists(): + if not image_file.images.exclude(galley=self).exists(): image_file.unlink_file() def __str__(self): @@ -1275,18 +1547,17 @@ def detail(self): def render(self, recover=False): return files.render_xml( - self.file, self.article, + self.file, + self.article, xsl_path=self.xsl_file.file.path, recover=recover, ) def render_crossref(self): xsl_path = os.path.join( - settings.BASE_DIR, 'transform', 'xsl', files.CROSSREF_XSL) - return files.render_xml( - self.file, self.article, - xsl_path=xsl_path + settings.BASE_DIR, "transform", "xsl", files.CROSSREF_XSL ) + return files.render_xml(self.file, self.article, xsl_path=xsl_path) def has_missing_image_files(self, show_all=False): if not self.file.mime_type in files.MIMETYPES_WITH_FIGURES: @@ -1294,12 +1565,12 @@ def has_missing_image_files(self, show_all=False): xml_file_contents = self.file.get_file(self.article) - souped_xml = BeautifulSoup(xml_file_contents, 'lxml') + souped_xml = BeautifulSoup(xml_file_contents, "lxml") elements = { - 'img': 'src', - 'graphic': 'xlink:href', - 'inline-graphic': 'xlink:href', + "img": "src", + "graphic": "xlink:href", + "inline-graphic": "xlink:href", } missing_elements = [] @@ -1338,8 +1609,8 @@ def file_content(self, dont_render=False, recover=False): return self.render(recover=recover) elif self.file.mime_type in files.IMAGE_MIMETYPES: url = reverse( - 'article_download_galley', - kwargs={"article_id": self.article.id, "galley_id": self.id} + "article_download_galley", + kwargs={"article_id": self.article.id, "galley_id": self.id}, ) contents = IMAGE_GALLEY_TEMPLATE.format( url=url, @@ -1348,9 +1619,10 @@ def file_content(self, dont_render=False, recover=False): return contents def path(self): - url = reverse('article_download_galley', - kwargs={'article_id': self.article.pk, - 'galley_id': self.pk}) + url = reverse( + "article_download_galley", + kwargs={"article_id": self.article.pk, "galley_id": self.pk}, + ) return self.article.journal.site_url(path=url) @staticmethod @@ -1358,7 +1630,7 @@ def mimetypes_with_figures(): return files.MIMETYPES_WITH_FIGURES def save(self, *args, **kwargs): - if self.type == 'xml' and not self.xsl_file: + if self.type == "xml" and not self.xsl_file: if self.article.journal: self.xsl_file = self.article.journal.xsl else: @@ -1378,12 +1650,14 @@ def upload_to_journal(instance, filename): class XSLFile(models.Model): file = models.FileField( upload_to=upload_to_journal, - storage=JanewayFileSystemStorage('files/xsl'), + storage=JanewayFileSystemStorage("files/xsl"), + ) + journal = models.ForeignKey( + "journal.Journal", on_delete=models.CASCADE, blank=True, null=True ) - journal = models.ForeignKey("journal.Journal", on_delete=models.CASCADE, - blank=True, null=True) date_uploaded = models.DateTimeField(default=timezone.now) - label = models.CharField(max_length=255, + label = models.CharField( + max_length=255, help_text="A label to help recognise this stylesheet", unique=True, ) @@ -1394,13 +1668,11 @@ def save(self, *args, **kwargs): super().save(*args, **kwargs) def __str__(self): - return "%s(%s@%s)" % ( - self.__class__.__name__, self.label, self.file.path) + return "%s(%s@%s)" % (self.__class__.__name__, self.label, self.file.path) def default_xsl(): - return XSLFile.objects.get( - label=settings.DEFAULT_XSL_FILE_LABEL).pk + return XSLFile.objects.get(label=settings.DEFAULT_XSL_FILE_LABEL).pk class SupplementaryFile(models.Model): @@ -1425,10 +1697,10 @@ def mime_type(self): def url(self): path = reverse( - 'article_download_supp_file', + "article_download_supp_file", kwargs={ - 'article_id': self.file.article.pk, - 'supp_file_id': self.pk, + "article_id": self.file.article.pk, + "supp_file_id": self.pk, }, ) @@ -1436,20 +1708,29 @@ def url(self): class Task(models.Model): - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='task_content_type', null=True) + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + related_name="task_content_type", + null=True, + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") title = models.CharField(max_length=300) description = JanewayBleachField() - complete_events = models.ManyToManyField('core.TaskCompleteEvents') - link = models.TextField(null=True, blank=True, help_text='A url name, where the action of this task can undertaken') + complete_events = models.ManyToManyField("core.TaskCompleteEvents") + link = models.TextField( + null=True, + blank=True, + help_text="A url name, where the action of this task can undertaken", + ) assignees = models.ManyToManyField(Account) completed_by = models.ForeignKey( Account, blank=True, null=True, - related_name='completed_by', + related_name="completed_by", on_delete=models.SET_NULL, ) @@ -1484,16 +1765,20 @@ def destroyer(**kwargs): # To militate against this risk, we recommend that task_obj is _always_ set to an article and that, likewise, # task.object is always an article. All other workflow components can be looked up from this point. - tasks_to_destroy = Task.objects.filter(complete_events__event_name=kwargs['event'], - content_type=ContentType.objects.get_for_model(kwargs['task_obj']), - object_id=kwargs['task_obj'].pk) + tasks_to_destroy = Task.objects.filter( + complete_events__event_name=kwargs["event"], + content_type=ContentType.objects.get_for_model(kwargs["task_obj"]), + object_id=kwargs["task_obj"].pk, + ) for task in tasks_to_destroy: task.completed = timezone.now() task.save() def __str__(self): - return "Task for {0} #{1}: {2}".format(self.content_type, self.object_id, self.title) + return "Task for {0} #{1}: {2}".format( + self.content_type, self.object_id, self.title + ) class TaskCompleteEvents(models.Model): @@ -1503,19 +1788,19 @@ def __str__(self): return self.event_name class Meta: - verbose_name_plural = 'task complete events' + verbose_name_plural = "task complete events" class EditorialGroup(models.Model): name = models.CharField(max_length=500) press = models.ForeignKey( - 'press.Press', + "press.Press", on_delete=models.CASCADE, default=default_press_id, ) description = JanewayBleachField(blank=True, null=True) journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, blank=True, null=True, @@ -1527,7 +1812,7 @@ class EditorialGroup(models.Model): ) class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def next_member_sequence(self): orderings = [member.sequence for member in self.editorialgroupmember_set.all()] @@ -1538,9 +1823,9 @@ def members(self): def __str__(self): if self.journal: - return f'{self.name} ({self.journal.code})' + return f"{self.name} ({self.journal.code})" else: - return f'{self.name} ({self.press})' + return f"{self.name} ({self.press})" class EditorialGroupMember(models.Model): @@ -1555,21 +1840,25 @@ class EditorialGroupMember(models.Model): sequence = models.PositiveIntegerField() statement = models.TextField( blank=True, - help_text='A statement of interest or purpose', + help_text="A statement of interest or purpose", ) class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): - return f'{self.user} in {self.group}' + return f"{self.user} in {self.group}" class Contacts(models.Model): - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, - related_name='contact_content_type', null=True) + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + related_name="contact_content_type", + null=True, + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") name = models.CharField(max_length=300) email = models.EmailField() @@ -1580,48 +1869,53 @@ class Meta: # This verbose name will hopefully more clearly # distinguish this model from the below model `Contact` # in the admin area. - verbose_name_plural = 'contacts' - ordering = ('sequence', 'name') + verbose_name_plural = "contacts" + ordering = ("sequence", "name") def __str__(self): return "{0}, {1} - {2}".format(self.name, self.object, self.role) class Contact(models.Model): - recipient = models.EmailField(max_length=200, verbose_name=_('Who would you like to contact?')) - sender = models.EmailField(max_length=200, verbose_name=_('Your contact email address')) - subject = models.CharField(max_length=300, verbose_name=_('Subject')) - body = JanewayBleachField(verbose_name=_('Your message')) + recipient = models.EmailField( + max_length=200, verbose_name=_("Who would you like to contact?") + ) + sender = models.EmailField( + max_length=200, verbose_name=_("Your contact email address") + ) + subject = models.CharField(max_length=300, verbose_name=_("Subject")) + body = JanewayBleachField(verbose_name=_("Your message")) client_ip = models.GenericIPAddressField() date_sent = models.DateField(auto_now_add=True) - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='contact_c_t', - null=True) + content_type = models.ForeignKey( + ContentType, on_delete=models.CASCADE, related_name="contact_c_t", null=True + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") class Meta: # This verbose name will hopefully more clearly # distinguish this model from the above model `Contacts` # in the admin area. - verbose_name_plural = 'contact messages' + verbose_name_plural = "contact messages" class DomainAlias(AbstractSiteModel): redirect = models.BooleanField( - default=True, - verbose_name="301", - help_text="If enabled, the site will throw a 301 redirect to the " - "master domain." + default=True, + verbose_name="301", + help_text="If enabled, the site will throw a 301 redirect to the " + "master domain.", ) journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", blank=True, null=True, on_delete=models.CASCADE, ) press = models.ForeignKey( - 'press.Press', + "press.Press", blank=True, null=True, on_delete=models.CASCADE, @@ -1633,65 +1927,72 @@ def site_object(self): @property def redirect_url(self): - return self.site_object.site_url() + return self.site_object.site_url() def build_redirect_url(self, path=None): - return self.site_object.site_url(path=path) + return self.site_object.site_url(path=path) def save(self, *args, **kwargs): if not bool(self.journal) ^ bool(self.press): - raise ValidationError( - " One and only one of press or journal must be set") + raise ValidationError(" One and only one of press or journal must be set") return super().save(*args, **kwargs) class Meta: - verbose_name_plural = 'domain aliases' + verbose_name_plural = "domain aliases" BASE_ELEMENTS = [ - {'name': 'review', - 'handshake_url': 'review_home', - 'jump_url': 'review_in_review', - 'stage': submission_models.STAGE_UNASSIGNED, - 'article_url': True}, - {'name': 'copyediting', - 'handshake_url': 'copyediting', - 'jump_url': 'article_copyediting', - 'stage': submission_models.STAGE_EDITOR_COPYEDITING, - 'article_url': True}, - {'name': 'production', - 'handshake_url': 'production_list', - 'jump_url': 'production_article', - 'stage': submission_models.STAGE_TYPESETTING, - 'article_url': False}, - {'name': 'proofing', - 'handshake_url': 'proofing_list', - 'jump_url': 'proofing_article', - 'stage': submission_models.STAGE_PROOFING, - 'article_url': False}, - {'name': 'prepublication', - 'handshake_url': 'publish', - 'jump_url': 'publish_article', - 'stage': submission_models.STAGE_READY_FOR_PUBLICATION, - 'article_url': True} + { + "name": "review", + "handshake_url": "review_home", + "jump_url": "review_in_review", + "stage": submission_models.STAGE_UNASSIGNED, + "article_url": True, + }, + { + "name": "copyediting", + "handshake_url": "copyediting", + "jump_url": "article_copyediting", + "stage": submission_models.STAGE_EDITOR_COPYEDITING, + "article_url": True, + }, + { + "name": "production", + "handshake_url": "production_list", + "jump_url": "production_article", + "stage": submission_models.STAGE_TYPESETTING, + "article_url": False, + }, + { + "name": "proofing", + "handshake_url": "proofing_list", + "jump_url": "proofing_article", + "stage": submission_models.STAGE_PROOFING, + "article_url": False, + }, + { + "name": "prepublication", + "handshake_url": "publish", + "jump_url": "publish_article", + "stage": submission_models.STAGE_READY_FOR_PUBLICATION, + "article_url": True, + }, ] -BASE_ELEMENT_NAMES = [ - element.get('name') for element in BASE_ELEMENTS -] +BASE_ELEMENT_NAMES = [element.get("name") for element in BASE_ELEMENTS] class Workflow(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) - elements = models.ManyToManyField('WorkflowElement') + elements = models.ManyToManyField("WorkflowElement") class WorkflowElement(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) element_name = models.CharField(max_length=255) @@ -1705,11 +2006,12 @@ class WorkflowElement(models.Model): order = models.PositiveIntegerField(default=20) class Meta: - ordering = ('order', 'element_name') + ordering = ("order", "element_name") @property def stages(self): from core import workflow + try: return workflow.ELEMENT_STAGES[self.element_name] except KeyError: @@ -1725,6 +2027,7 @@ def articles(self): @property def settings(self): from core import workflow + return workflow.workflow_plugin_settings(self) def __str__(self): @@ -1733,7 +2036,7 @@ def __str__(self): class WorkflowLog(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) element = models.ForeignKey( @@ -1743,10 +2046,10 @@ class WorkflowLog(models.Model): timestamp = models.DateTimeField(default=timezone.now) class Meta: - ordering = ('timestamp',) + ordering = ("timestamp",) def __str__(self): - return '{0} {1}'.format(self.element.element_name, self.timestamp) + return "{0} {1}".format(self.element.element_name, self.timestamp) class HomepageElement(models.Model): @@ -1763,16 +2066,20 @@ class HomepageElement(models.Model): sequence = models.PositiveIntegerField(default=999) # the associated object - content_type = models.ForeignKey(ContentType, - on_delete=models.CASCADE, - related_name='element_content_type', - null=True) + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + related_name="element_content_type", + null=True, + ) object_id = models.PositiveIntegerField(blank=True, null=True) - object = GenericForeignKey('content_type', 'object_id') + object = GenericForeignKey("content_type", "object_id") - available_to_press = models.BooleanField(default=False, help_text='Determines if this element is ' - 'available for the press.') + available_to_press = models.BooleanField( + default=False, + help_text="Determines if this element is " "available for the press.", + ) # whether or not this item is active active = models.BooleanField(default=False) @@ -1781,9 +2088,9 @@ class HomepageElement(models.Model): has_config = models.BooleanField(default=True) class Meta: - verbose_name_plural = 'Homepage Elements' - ordering = ('sequence', 'name') - unique_together = ('name', 'content_type', 'object_id') + verbose_name_plural = "Homepage Elements" + ordering = ("sequence", "name") + unique_together = ("name", "content_type", "object_id") def __str__(self): return self.name @@ -1797,23 +2104,23 @@ class LoginAttempt(models.Model): class AccessRequest(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", blank=True, null=True, on_delete=models.CASCADE, ) repository = models.ForeignKey( - 'repository.Repository', + "repository.Repository", blank=True, null=True, on_delete=models.CASCADE, ) user = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) role = models.ForeignKey( - 'core.Role', + "core.Role", on_delete=models.CASCADE, ) requested = models.DateTimeField( @@ -1828,11 +2135,11 @@ class AccessRequest(models.Model): ) evaluation_note = JanewayBleachField( null=True, - help_text='This note will be sent to the requester when you approve or decline their request.', + help_text="This note will be sent to the requester when you approve or decline their request.", ) def __str__(self): - return 'User {} requested {} permission for {}'.format( + return "User {} requested {} permission for {}".format( self.user.full_name(), self.journal.name if self.journal else self.repository.name, self.role.name, @@ -1848,6 +2155,7 @@ def setup_user_signature(sender, instance, created, **kwargs): # This model is vestigial and will be removed in v1.5 + class SettingValueTranslation(models.Model): hvad_value = models.TextField( blank=True, @@ -1860,12 +2168,13 @@ class SettingValueTranslation(models.Model): class Meta: managed = False - db_table = 'core_settingvalue_translation' + db_table = "core_settingvalue_translation" def log_hijack_started(sender, hijacker, hijacked, request, **kwargs): from utils import models as utils_models - action = '{} ({}) has hijacked {} ({})'.format( + + action = "{} ({}) has hijacked {} ({})".format( hijacker.full_name(), hijacker.pk, hijacked.full_name(), @@ -1873,18 +2182,19 @@ def log_hijack_started(sender, hijacker, hijacked, request, **kwargs): ) utils_models.LogEntry.add_entry( - types='Hijack Start', + types="Hijack Start", description=action, - level='Info', + level="Info", actor=hijacker, request=request, - target=hijacked + target=hijacked, ) def log_hijack_ended(sender, hijacker, hijacked, request, **kwargs): from utils import models as utils_models - action = '{} ({}) has released {} ({})'.format( + + action = "{} ({}) has released {} ({})".format( hijacker.full_name(), hijacker.pk, hijacked.full_name(), @@ -1892,12 +2202,12 @@ def log_hijack_ended(sender, hijacker, hijacked, request, **kwargs): ) utils_models.LogEntry.add_entry( - types='Hijack Release', + types="Hijack Release", description=action, - level='Info', + level="Info", actor=hijacker, request=request, - target=hijacked + target=hijacked, ) diff --git a/src/core/plugin_installed_apps.py b/src/core/plugin_installed_apps.py index 4cb48ec748..da481f1570 100755 --- a/src/core/plugin_installed_apps.py +++ b/src/core/plugin_installed_apps.py @@ -10,32 +10,45 @@ def load_plugin_apps(base_dir): path = os.path.join(base_dir, "plugins") root, dirs, files = next(os.walk(path)) - return ['plugins.{0}'.format(dir) for dir in dirs if dir != '__pycache__'] + return ["plugins.{0}".format(dir) for dir in dirs if dir != "__pycache__"] def load_plugin_templates(base_dir): path = os.path.join(base_dir, "plugins") root, dirs, files = next(os.walk(path)) - return ['{0}/plugins/{1}/templates/'.format(base_dir, dir) for dir in dirs if dir != '__pycache__'] + return [ + "{0}/plugins/{1}/templates/".format(base_dir, dir) + for dir in dirs + if dir != "__pycache__" + ] def load_homepage_element_apps(base_dir): path = os.path.join(base_dir, "core", "homepage_elements") root, dirs, files = next(os.walk(path)) - return ['core.homepage_elements.{0}'.format(dir) for dir in dirs if dir != '__pycache__'] + return [ + "core.homepage_elements.{0}".format(dir) for dir in dirs if dir != "__pycache__" + ] def load_homepage_element_templates(base_dir): path = os.path.join(base_dir, "core", "homepage_elements") root, dirs, files = next(os.walk(path)) - return ['{0}/core/homepage_elements/{1}/templates/'.format( - base_dir, dir) for dir in dirs if dir != '__pycache__'] + return [ + "{0}/core/homepage_elements/{1}/templates/".format(base_dir, dir) + for dir in dirs + if dir != "__pycache__" + ] def load_plugin_locales(base_dir): path = os.path.join(base_dir, "plugins") root, dirs, files = next(os.walk(path)) - return [os.path.join(base_dir, 'plugins', dir, 'locales') for dir in dirs if dir != '__pycache__'] + return [ + os.path.join(base_dir, "plugins", dir, "locales") + for dir in dirs + if dir != "__pycache__" + ] diff --git a/src/core/plugin_loader.py b/src/core/plugin_loader.py index c8ef710029..859286f633 100755 --- a/src/core/plugin_loader.py +++ b/src/core/plugin_loader.py @@ -21,7 +21,7 @@ def get_dirs(directory): path = os.path.join(settings.BASE_DIR, directory) root, dirs, files = next(os.walk(path)) - dirs = [x for x in dirs if x != '__pycache__'] + dirs = [x for x in dirs if x != "__pycache__"] return dirs @@ -72,11 +72,9 @@ def load(directory="plugins", prefix="plugins", permissive=False): PLUGIN_WORKFLOW_STAGES.append( (plugin_settings.STAGE, plugin_settings.PLUGIN_NAME) ) - ELEMENT_STAGES[ - plugin_settings.PLUGIN_NAME] = [plugin_settings.STAGE] + ELEMENT_STAGES[plugin_settings.PLUGIN_NAME] = [plugin_settings.STAGE] - STAGES_ELEMENTS[ - plugin_settings.STAGE] = plugin_settings.PLUGIN_NAME + STAGES_ELEMENTS[plugin_settings.STAGE] = plugin_settings.PLUGIN_NAME # Call event registry register_for_events(plugin_settings) @@ -116,13 +114,13 @@ def get_plugin(module_name, permissive): plugin = models.Plugin.objects.get(name=module_name, enabled=True) return plugin except ( - models.Plugin.DoesNotExist, - models.Plugin.MultipleObjectsReturned, - ProgrammingError, - OperationalError, - ) as e: + models.Plugin.DoesNotExist, + models.Plugin.MultipleObjectsReturned, + ProgrammingError, + OperationalError, + ) as e: if settings.DEBUG: - print('Error loading plugin {0} {1}'.format(module_name, e)) + print("Error loading plugin {0} {1}".format(module_name, e)) return False diff --git a/src/core/templatetags/bool_fa.py b/src/core/templatetags/bool_fa.py index 77b429a555..63ad1387b7 100755 --- a/src/core/templatetags/bool_fa.py +++ b/src/core/templatetags/bool_fa.py @@ -4,7 +4,7 @@ register = template.Library() -@register.filter(name='bool_fa') +@register.filter(name="bool_fa") def bool_fa(boolean): if boolean: return mark_safe('') diff --git a/src/core/templatetags/classname.py b/src/core/templatetags/classname.py index 806ce06b50..89ee3a27b3 100644 --- a/src/core/templatetags/classname.py +++ b/src/core/templatetags/classname.py @@ -8,4 +8,4 @@ def classname(obj): try: return obj.__class__.__name__ except AttributeError: - return '' + return "" diff --git a/src/core/templatetags/dates.py b/src/core/templatetags/dates.py index 387e092a6a..b688eae2e3 100644 --- a/src/core/templatetags/dates.py +++ b/src/core/templatetags/dates.py @@ -4,6 +4,7 @@ register = template.Library() + @register.simple_tag def offset_date(days=0, input_type="date"): """ diff --git a/src/core/templatetags/debug_tools.py b/src/core/templatetags/debug_tools.py index 8be371392b..d41237a61c 100644 --- a/src/core/templatetags/debug_tools.py +++ b/src/core/templatetags/debug_tools.py @@ -17,8 +17,9 @@ class TraceNode(template.Node): def render(self, context): import pdb + pdb.set_trace() - return '' + return "" @register.tag diff --git a/src/core/templatetags/dict.py b/src/core/templatetags/dict.py index 75abde3467..f9289040f1 100755 --- a/src/core/templatetags/dict.py +++ b/src/core/templatetags/dict.py @@ -3,12 +3,12 @@ register = template.Library() -@register.filter(name='get') +@register.filter(name="get") def get(o, index): try: return o[str(index)] except BaseException: - return 'INVALID STRING' + return "INVALID STRING" @register.simple_tag @@ -16,4 +16,4 @@ def tag_get(o, index): try: return o[str(index)] except BaseException: - return 'INVALID STRING' + return "INVALID STRING" diff --git a/src/core/templatetags/escaping.py b/src/core/templatetags/escaping.py index 0dcf875a28..b3056e9f9b 100644 --- a/src/core/templatetags/escaping.py +++ b/src/core/templatetags/escaping.py @@ -4,9 +4,10 @@ register = template.Library() + @register.filter def unescape(value): - """ Unescapes all HTML tags and entities + """Unescapes all HTML tags and entities Can be used with `striptags` to ensure entities are turned into plain text e.g : {{ some_html | striptags | unescape}} """ diff --git a/src/core/templatetags/files.py b/src/core/templatetags/files.py index ce98a39885..56441ff679 100755 --- a/src/core/templatetags/files.py +++ b/src/core/templatetags/files.py @@ -23,12 +23,9 @@ def file_size(file, article): def has_missing_supplements(galley): xml_file_contents = galley.file.get_file(galley.article) - souped_xml = BeautifulSoup(xml_file_contents, 'lxml') + souped_xml = BeautifulSoup(xml_file_contents, "lxml") - elements = { - 'img': 'src', - 'graphic': 'xlink:href' - } + elements = {"img": "src", "graphic": "xlink:href"} missing_elements = [] @@ -54,17 +51,13 @@ def has_missing_supplements(galley): @register.simple_tag() def file_type(article, file): - from production.logic import get_copyedit_files + copyedited_files = get_copyedit_files(article) galley_files = {galley.file for galley in article.galley_set.all()} - supplementary_files = { - supp.file for supp in article.supplementary_files.all() - } + supplementary_files = {supp.file for supp in article.supplementary_files.all()} galley_sub_files = list() - review_files = { - review.review_file for review in article.reviewassignment_set.all() - } + review_files = {review.review_file for review in article.reviewassignment_set.all()} try: # GalleyProofing belongs to the typesetting plugin annotated_files = models.File.objects.filter( @@ -82,29 +75,27 @@ def file_type(article, file): galley_sub_files.append(galley.css_file) if file in galley_files: - return 'Galley' + return "Galley" if file in supplementary_files: - return 'Supplementary' + return "Supplementary" if file in article.manuscript_files.all(): - return 'Manuscript' + return "Manuscript" if file in article.data_figure_files.all(): - return 'Data/Figure' + return "Data/Figure" if file in annotated_files: - return 'Proofing' + return "Proofing" if file in copyedited_files: - return 'Copyedit' + return "Copyedit" if file in galley_sub_files: - return 'Galley Sub File' + return "Galley Sub File" if file in review_files: - return 'Review Comment' - return 'Other' + return "Review Comment" + return "Other" + @register.filter() def filter_html_downloads(galleys): if galleys and galleys[0].article.journal.disable_html_downloads is True: - galleys = [ - galley for galley in galleys - if galley.file.label != 'HTML' - ] + galleys = [galley for galley in galleys if galley.file.label != "HTML"] return galleys diff --git a/src/core/templatetags/fqdn.py b/src/core/templatetags/fqdn.py index c828b5289a..1766482909 100755 --- a/src/core/templatetags/fqdn.py +++ b/src/core/templatetags/fqdn.py @@ -6,7 +6,7 @@ @register.simple_tag(takes_context=True) def journal_url(context, url_name=None, *args): - request = context.get('request') + request = context.get("request") if url_name is not None: path = reverse(url_name, args=args) else: @@ -17,9 +17,10 @@ def journal_url(context, url_name=None, *args): else: return path + @register.simple_tag(takes_context=True) def repository_url(context, url_name=None, *args): - request = context.get('request') + request = context.get("request") if url_name is not None: path = reverse(url_name, args=args) else: @@ -30,9 +31,10 @@ def repository_url(context, url_name=None, *args): else: return path + @register.simple_tag(takes_context=True) def site_url(context, url_name=None, *args): - request = context.get('request') + request = context.get("request") if url_name is not None: path = reverse(url_name, args=args) else: @@ -61,5 +63,5 @@ def external_journal_url(journal, url_name=None, *args): @register.simple_tag(takes_context=True) def build_absolute_uri(context, relative_url, *args): - request = context.get('request') + request = context.get("request") return request.build_absolute_uri(relative_url) diff --git a/src/core/templatetags/hooks.py b/src/core/templatetags/hooks.py index 86820c4093..9ebade9537 100755 --- a/src/core/templatetags/hooks.py +++ b/src/core/templatetags/hooks.py @@ -13,12 +13,12 @@ @register.simple_tag(takes_context=True) def hook(context, hook_name, *args, **kwargs): try: - html = '' + html = "" for hook in settings.PLUGIN_HOOKS.get(hook_name, []): - hook_module = import_module(hook.get('module')) - function = getattr(hook_module, hook.get('function')) + hook_module = import_module(hook.get("module")) + function = getattr(hook_module, hook.get("function")) html = html + function(context, *args, **kwargs) return mark_safe(html) except Exception as e: - logger.error('Error rendering hook {0}: {1}'.format(hook_name, e)) + logger.error("Error rendering hook {0}: {1}".format(hook_name, e)) diff --git a/src/core/templatetags/itertools.py b/src/core/templatetags/itertools.py index 82cb89c58b..208eab4194 100644 --- a/src/core/templatetags/itertools.py +++ b/src/core/templatetags/itertools.py @@ -4,6 +4,6 @@ register = template.Library() -@register.simple_tag(name='chain') +@register.simple_tag(name="chain") def chain_tag(*iterables): return chain(*iterables) diff --git a/src/core/templatetags/orcid.py b/src/core/templatetags/orcid.py index a44a11ea3f..7f491ba471 100644 --- a/src/core/templatetags/orcid.py +++ b/src/core/templatetags/orcid.py @@ -7,9 +7,8 @@ @register.simple_tag(takes_context=True) def orcid_redirect_uri(context, action="login"): - request = context.get('request') + request = context.get("request") if request: return build_redirect_uri(request.site_type, action=action) else: return "" - diff --git a/src/core/templatetags/pages.py b/src/core/templatetags/pages.py index c5dcb1aea5..1d9f5f419f 100644 --- a/src/core/templatetags/pages.py +++ b/src/core/templatetags/pages.py @@ -1,6 +1,7 @@ """ Template tags for handling paginator objects """ + from django import template register = template.Library() @@ -8,7 +9,7 @@ @register.filter() def slice_pages(current_page, slice_size=2): - """ Given a page and a slice size returns a slice of pages + """Given a page and a slice size returns a slice of pages :current_page: A django.core.paginator.Page :slice_size: int @@ -18,10 +19,10 @@ def slice_pages(current_page, slice_size=2): first = max(1, current_page.number - slice_size) last = min(paginator.num_pages, current_page.number + slice_size) return [ - current_page.paginator.page(page_num) - for page_num in range(first, last + 1) + current_page.paginator.page(page_num) for page_num in range(first, last + 1) ] + @register.filter() def slice_pages_with_first_last_ellipsis(current_page, slice_size=2): paginator = current_page.paginator @@ -31,7 +32,7 @@ def slice_pages_with_first_last_ellipsis(current_page, slice_size=2): if current_page.number - slice_size > 1: page_set.append(paginator.page(1)) if current_page.number - slice_size > 3: - page_set.append('...') + page_set.append("...") elif current_page.number - slice_size > 2: page_set.append(paginator.page(2)) @@ -39,7 +40,7 @@ def slice_pages_with_first_last_ellipsis(current_page, slice_size=2): if current_page.number + slice_size < paginator.num_pages: if current_page.number + slice_size < paginator.num_pages - 2: - page_set.append('...') + page_set.append("...") elif current_page.number + slice_size < paginator.num_pages - 1: page_set.append(paginator.page(paginator.num_pages - 1)) page_set.append(paginator.page(paginator.num_pages)) diff --git a/src/core/templatetags/render_string.py b/src/core/templatetags/render_string.py index 21e48144e9..0d85d820eb 100644 --- a/src/core/templatetags/render_string.py +++ b/src/core/templatetags/render_string.py @@ -5,7 +5,6 @@ @register.simple_tag(takes_context=True) def render_string(context, string): - """ Renders the given string as with django Template engine""" - templ = template.Template(string) - return templ.render(context) - + """Renders the given string as with django Template engine""" + templ = template.Template(string) + return templ.render(context) diff --git a/src/core/templatetags/roles.py b/src/core/templatetags/roles.py index 7d718a86f1..7ba73791aa 100755 --- a/src/core/templatetags/roles.py +++ b/src/core/templatetags/roles.py @@ -10,11 +10,7 @@ def user_has_role(request, role, staff_override=True): if not request.user.is_authenticated: return None - return request.user.check_role( - request.journal, - role, - staff_override=staff_override - ) + return request.user.check_role(request.journal, role, staff_override=staff_override) @register.simple_tag @@ -22,16 +18,11 @@ def user_roles(journal, user, slugs=False): if slugs: return [ ar.role.slug - for ar in models.AccountRole.objects.filter( - user=user, journal=journal - ) + for ar in models.AccountRole.objects.filter(user=user, journal=journal) ] else: return [ - ar - for ar in models.AccountRole.objects.filter( - user=user, journal=journal - ) + ar for ar in models.AccountRole.objects.filter(user=user, journal=journal) ] diff --git a/src/core/templatetags/settings.py b/src/core/templatetags/settings.py index a52fb857c7..0d6a11c524 100755 --- a/src/core/templatetags/settings.py +++ b/src/core/templatetags/settings.py @@ -16,7 +16,7 @@ def get_setting(journal, setting_name): ).processed_value return setting_value except models.Setting.DoesNotExist: - return '' + return "" @register.filter diff --git a/src/core/templatetags/text.py b/src/core/templatetags/text.py index 4baafe6888..ca4182e652 100644 --- a/src/core/templatetags/text.py +++ b/src/core/templatetags/text.py @@ -3,7 +3,7 @@ register = template.Library() -@register.filter('startswith') +@register.filter("startswith") def startswith(text, starts): if isinstance(text, str): return text.startswith(starts) diff --git a/src/core/templatetags/translations.py b/src/core/templatetags/translations.py index 51e725ca13..7eed97962b 100644 --- a/src/core/templatetags/translations.py +++ b/src/core/templatetags/translations.py @@ -7,14 +7,14 @@ @register.filter def language_name(language_code): """ - Takes a language code eg en and returns its name from settings. + Takes a language code eg en and returns its name from settings. - {% load translations %} + {% load translations %} - {{ string|language_name }} - """ + {{ string|language_name }} + """ try: info = LANG_INFO.get(language_code) - return info.get('name_local') + return info.get("name_local") except (KeyError, AttributeError): return language_code diff --git a/src/core/templatetags/truncate.py b/src/core/templatetags/truncate.py index a8e7a5fb6b..97aba13489 100755 --- a/src/core/templatetags/truncate.py +++ b/src/core/templatetags/truncate.py @@ -15,7 +15,7 @@ def truncatesmart(value, limit=80): {{ string|truncatesmart:100 }} """ if not value: - return '' + return "" try: limit = int(limit) @@ -26,6 +26,6 @@ def truncatesmart(value, limit=80): return mark_safe(value) value = value[:limit] - words = value.split(' ')[:-1] + words = value.split(" ")[:-1] - return mark_safe(' '.join(words) + ' [...]') + return mark_safe(" ".join(words) + " [...]") diff --git a/src/core/templatetags/uuid.py b/src/core/templatetags/uuid.py index 81aa7bb4dd..0d95aca274 100644 --- a/src/core/templatetags/uuid.py +++ b/src/core/templatetags/uuid.py @@ -3,6 +3,7 @@ register = template.Library() + @register.simple_tag def get_uuid4(): - return f'u{str(uuid4())}' + return f"u{str(uuid4())}" diff --git a/src/core/tests/test_app.py b/src/core/tests/test_app.py index 0e8fbc6ecf..c5b149d827 100755 --- a/src/core/tests/test_app.py +++ b/src/core/tests/test_app.py @@ -21,6 +21,7 @@ from submission import models as submission_models import mock + class CoreTests(TestCase): """ Regression tests for the core application. @@ -28,105 +29,100 @@ class CoreTests(TestCase): @override_settings(URL_CONFIG="domain") def test_create_user_form(self): - data = { - 'email': 'test@test.com', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': 235, + "email": "test@test.com", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": 235, } self.client.force_login(self.admin_user) - response = self.client.post(reverse('core_add_user'), data) + response = self.client.post(reverse("core_add_user"), data) try: - models.Account.objects.get(email='test@test.com') + models.Account.objects.get(email="test@test.com") except models.Account.DoesNotExist: - self.fail('User account has not been saved.') - + self.fail("User account has not been saved.") @override_settings(URL_CONFIG="domain") def test_create_user_form_mixed_case(self): data = { - 'email': 'test@test.com', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': 235, + "email": "test@test.com", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": 235, } new_email = "TeSt@test.com" self.client.force_login(self.admin_user) - response_1 = self.client.post(reverse('core_add_user'), data) + response_1 = self.client.post(reverse("core_add_user"), data) response_2 = self.client.post( - reverse('core_add_user'), + reverse("core_add_user"), dict(data, email=new_email), ) try: - models.Account.objects.get(email='test@test.com') + models.Account.objects.get(email="test@test.com") except models.Account.DoesNotExist: - self.fail('User account has not been saved.') + self.fail("User account has not been saved.") self.assertEqual(response_2.status_code, 200) - @override_settings(URL_CONFIG="domain") def test_create_user_form_normalise_email(self): - data = { - 'email': 'Test@TEST.com', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': 235, + "email": "Test@TEST.com", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": 235, } self.client.force_login(self.admin_user) - response = self.client.post(reverse('core_add_user'), data) + response = self.client.post(reverse("core_add_user"), data) try: - models.Account.objects.get(email='Test@test.com') + models.Account.objects.get(email="Test@test.com") except models.Account.DoesNotExist: - self.fail('User account has not been saved.') + self.fail("User account has not been saved.") @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_mixed_case_email_address_correct_username(self): - data = { - 'email': 'MiXeDcAsE@TEST.com', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', + "email": "MiXeDcAsE@TEST.com", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", } - response = self.client.post(reverse('core_register'), data) + response = self.client.post(reverse("core_register"), data) try: - models.Account.objects.get(username='mixedcase@test.com') + models.Account.objects.get(username="mixedcase@test.com") except models.Account.DoesNotExist: - self.fail('Username has not been set to lowercase.') + self.fail("Username has not been set to lowercase.") @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_mixed_case_email_address_no_duplicates(self): @@ -135,71 +131,78 @@ def test_mixed_case_email_address_no_duplicates(self): other_email = email.lower() data = { - 'email': email, - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', + "email": email, + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", } - response = self.client.post(reverse('core_register'), data) + response = self.client.post(reverse("core_register"), data) try: - models.Account.objects.get(username='mixedcase@test.com') + models.Account.objects.get(username="mixedcase@test.com") except models.Account.DoesNotExist: - self.fail('Username has not been set to lowercase.') + self.fail("Username has not been set to lowercase.") with self.assertRaises( IntegrityError, - msg="Managed to issue accounts for %s and %s" - "" % (email, other_email), + msg="Managed to issue accounts for %s and %s" "" % (email, other_email), ): models.Account.objects.create(email=other_email) - @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_mixed_case_login_same_case(self): email = "Janeway@voyager.com" password = "random_password" data = { - 'email': email, - 'is_active': True, - 'password_1': password, - 'password_2': password, - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', + "email": email, + "is_active": True, + "password_1": password, + "password_2": password, + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", } - response = self.client.post(reverse('core_register'), data) + response = self.client.post(reverse("core_register"), data) account = models.Account.objects.get(email=email) account.is_active = True account.save() data = {"user_name": email, "user_pass": password} response = self.client.post( - reverse("core_login"), data, - HTTP_USER_AGENT='Mozilla/5.0', + reverse("core_login"), + data, + HTTP_USER_AGENT="Mozilla/5.0", ) self.assertEqual( - self.client.session["_auth_user_id"], str(account.pk), - msg="Registered user %s can't login with email %s" - "" % (email, email), + self.client.session["_auth_user_id"], + str(account.pk), + msg="Registered user %s can't login with email %s" "" % (email, email), ) - orcid_record = {'orcid': "0000-0000-0000-0000", 'uri': "http://sandbox.orcid.org/0000-0000-0000-0000", 'emails': ["campbell@evu.edu"], 'last_name': 'Kasey', 'first_name': 'Campbell', 'affiliation': 'Elk Valley University', 'country': 'US'} + orcid_record = { + "orcid": "0000-0000-0000-0000", + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "emails": ["campbell@evu.edu"], + "last_name": "Kasey", + "first_name": "Campbell", + "affiliation": "Elk Valley University", + "country": "US", + } @override_settings(CAPTCHA_TYPE=None) - @mock.patch('utils.orcid.get_orcid_record_details', return_value=orcid_record) + @mock.patch("utils.orcid.get_orcid_record_details", return_value=orcid_record) def test_orcid_registration(self, record_mock): orcid_id = "0000-0000-0000-0000" - token = models.OrcidToken.objects.create(orcid=orcid_id) + token = models.OrcidToken.objects.create(orcid=orcid_id) register_url = f"{reverse('core_register')}?token={token.token}" response = self.client.get(register_url) @@ -210,12 +213,28 @@ def test_orcid_registration(self, record_mock): self.assertContains(response, "campbell@evu.edu") self.assertNotContains(response, "Register with ORCiD") self.assertContains(response, "http://sandbox.orcid.org/0000-0000-0000-0000") - self.assertContains(response, '') + self.assertContains( + response, + '', + ) - response = self.client.post(register_url, {'first_name': 'Campbell', 'last_name': 'Kasey', 'email': "campbell@evu.edu", 'institution': "Elk Valley University", 'password_1': 'test_password', 'password_2': 'test_password', 'orcid': "0000-0000-0000-0000"}) + response = self.client.post( + register_url, + { + "first_name": "Campbell", + "last_name": "Kasey", + "email": "campbell@evu.edu", + "institution": "Elk Valley University", + "password_1": "test_password", + "password_2": "test_password", + "orcid": "0000-0000-0000-0000", + }, + ) - self.assertTrue(models.Account.objects.filter(email='campbell@evu.edu').exists()) - a = models.Account.objects.get(email='campbell@evu.edu') + self.assertTrue( + models.Account.objects.filter(email="campbell@evu.edu").exists() + ) + a = models.Account.objects.get(email="campbell@evu.edu") self.assertEqual(a.first_name, "Campbell") self.assertEqual(a.last_name, "Kasey") self.assertEqual(a.institution, "Elk Valley University") @@ -223,13 +242,13 @@ def test_orcid_registration(self, record_mock): self.assertEqual(a.orcid, orcid_id) def test_registration(self): - response = self.client.get(reverse('core_register')) + response = self.client.get(reverse("core_register")) self.assertEqual(response.status_code, 200) self.assertContains(response, "Register with ORCiD") @override_settings(ENABLE_ORCID=False) def test_registration(self): - response = self.client.get(reverse('core_register')) + response = self.client.get(reverse("core_register")) self.assertEqual(response.status_code, 200) self.assertNotContains(response, "Register with ORCiD") @@ -240,128 +259,134 @@ def test_mixed_case_login_different_case(self): password = "random_password" data = { - 'email': email, - 'is_active': True, - 'password_1': password, - 'password_2': password, - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', + "email": email, + "is_active": True, + "password_1": password, + "password_2": password, + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", } - response = self.client.post(reverse('core_register'), data) + response = self.client.post(reverse("core_register"), data) account = models.Account.objects.get(email=email) account.is_active = True account.save() data = {"user_name": login_email, "user_pass": password} response = self.client.post( - reverse("core_login"), data, - HTTP_USER_AGENT='Mozilla/5.0', + reverse("core_login"), + data, + HTTP_USER_AGENT="Mozilla/5.0", ) self.assertEqual( - self.client.session["_auth_user_id"], str(account.pk), + self.client.session["_auth_user_id"], + str(account.pk), msg="Registered user %s can't login with email %s" - "" % (email, login_email), + "" % (email, login_email), ) @override_settings(URL_CONFIG="domain") def test_create_user_form_mixed_case(self): data = { - 'email': 'test@test.com', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': 235, + "email": "test@test.com", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": 235, } new_email = "TeSt@test.com" self.client.force_login(self.admin_user) - response_1 = self.client.post(reverse('core_add_user'), data) + response_1 = self.client.post(reverse("core_add_user"), data) response_2 = self.client.post( - reverse('core_add_user'), + reverse("core_add_user"), dict(data, email=new_email), ) try: - models.Account.objects.get(email='test@test.com') + models.Account.objects.get(email="test@test.com") except models.Account.DoesNotExist: - self.fail('User account has not been saved.') + self.fail("User account has not been saved.") self.assertEqual(response_2.status_code, 200) @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_register_as_reader(self): setting_handler.save_setting( - setting_group_name='notifications', - setting_name='send_reader_notifications', + setting_group_name="notifications", + setting_name="send_reader_notifications", journal=self.journal_one, - value='On', + value="On", ) data = { - 'email': 'reader@janeway.systems', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', - 'register_as_reader': True, + "email": "reader@janeway.systems", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", + "register_as_reader": True, } clear_script_prefix() clear_cache() - response = self.client.post(reverse('core_register'), data, SERVER_NAME='testserver') - user = models.Account.objects.get(username='reader@janeway.systems') + response = self.client.post( + reverse("core_register"), data, SERVER_NAME="testserver" + ) + user = models.Account.objects.get(username="reader@janeway.systems") role_check = user.check_role( self.journal_one, - 'reader', + "reader", ) self.assertTrue(role_check) @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_sending_reader_notification(self): setting_handler.save_setting( - setting_group_name='notifications', - setting_name='send_reader_notifications', + setting_group_name="notifications", + setting_name="send_reader_notifications", journal=self.journal_one, - value='On', + value="On", ) - reader_email = 'another_reader@janeway.systems' + reader_email = "another_reader@janeway.systems" data = { - 'email': reader_email, - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', - 'register_as_reader': True, + "email": reader_email, + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", + "register_as_reader": True, } clear_script_prefix() clear_cache() - response = self.client.post(reverse('core_register'), data, SERVER_NAME='testserver') + response = self.client.post( + reverse("core_register"), data, SERVER_NAME="testserver" + ) - call_command('send_publication_notifications', self.article_two.journal.code) + call_command("send_publication_notifications", self.article_two.journal.code) email = None for message in mail.outbox: - if message.subject == '[TST] New Articles Published': + if message.subject == "[TST] New Articles Published": email = message self.assertTrue(email) @@ -369,48 +394,44 @@ def test_sending_reader_notification(self): @override_settings(URL_CONFIG="domain", CAPTCHA_TYPE=None) def test_register_without_reader(self): - data = { - 'email': 'reader@janeway.systems', - 'is_active': True, - 'password_1': 'this_is_a_password', - 'password_2': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', - 'country': '', - 'register_as_reader': False, + "email": "reader@janeway.systems", + "is_active": True, + "password_1": "this_is_a_password", + "password_2": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", + "country": "", + "register_as_reader": False, } - response = self.client.post(reverse('core_register'), data, SERVER_NAME='testserver') - user = models.Account.objects.get(username='reader@janeway.systems') + response = self.client.post( + reverse("core_register"), data, SERVER_NAME="testserver" + ) + user = models.Account.objects.get(username="reader@janeway.systems") role_check = user.check_role( self.journal_one, - 'reader', + "reader", ) self.assertFalse(role_check) def test_email_subjects(self): - email_settings= models.Setting.objects.filter( + email_settings = models.Setting.objects.filter( group__name="email", ).values_list("name", flat=True) subject_settings = models.Setting.objects.filter( group__name="email_subject", ).values_list("name", flat=True) - missing = ( - set(email_settings) - - {s[len("subject_"):] for s in subject_settings} - ) - self.assertFalse( - missing, - msg="Found emails that don't have a subject setting") + missing = set(email_settings) - {s[len("subject_") :] for s in subject_settings} + self.assertFalse(missing, msg="Found emails that don't have a subject setting") - @patch.object(models.Setting, 'validate') + @patch.object(models.Setting, "validate") def test_setting_validation(self, mock_method): setting_handler.save_setting( - 'email', 'editor_assignment', self.journal_one, 'test' + "email", "editor_assignment", self.journal_one, "test" ) mock_method.assert_called() @@ -423,27 +444,22 @@ def test_hide_review_details_on(self): clear_cache() review_models.ReviewAssignment.objects.filter( article=self.article_one, - ).update( - for_author_consumption=False - ) + ).update(for_author_consumption=False) self.client.force_login( self.article_one.owner, ) response = self.client.get( reverse( - 'core_dashboard_article', + "core_dashboard_article", kwargs={ - 'article_id': self.article_one.pk, - } + "article_id": self.article_one.pk, + }, ), SERVER_NAME="testserver", ) self.assertEqual(response.status_code, 200) - self.assertNotContains( - response, - 'Reviews' - ) + self.assertNotContains(response, "Reviews") @override_settings(URL_CONFIG="domain") def test_peer_reviews_for_author_consumption_overrides_hide_review_data(self): @@ -451,27 +467,22 @@ def test_peer_reviews_for_author_consumption_overrides_hide_review_data(self): clear_cache() review_models.ReviewAssignment.objects.filter( article=self.article_one, - ).update( - for_author_consumption=True - ) + ).update(for_author_consumption=True) self.client.force_login( self.article_one.owner, ) response = self.client.get( reverse( - 'core_dashboard_article', + "core_dashboard_article", kwargs={ - 'article_id': self.article_one.pk, - } + "article_id": self.article_one.pk, + }, ), SERVER_NAME="testserver", ) self.assertEqual(response.status_code, 200) - self.assertContains( - response, - 'Reviews' - ) + self.assertContains(response, "Reviews") @override_settings(URL_CONFIG="domain") def test_enable_peer_review_data_block(self): @@ -480,46 +491,53 @@ def test_enable_peer_review_data_block(self): # Test will fail without a review assignment available review_models.ReviewAssignment.objects.filter( article=self.article_one, - ).update( - for_author_consumption=True - ) + ).update(for_author_consumption=True) # Set setting being tested setting_handler.save_setting( - 'general', 'enable_peer_review_data_block', self.journal_one, 'on' + "general", "enable_peer_review_data_block", self.journal_one, "on" ) self.client.force_login( self.article_one.owner, ) response = self.client.get( reverse( - 'core_dashboard_article', + "core_dashboard_article", kwargs={ - 'article_id': self.article_one.pk, - } + "article_id": self.article_one.pk, + }, ), SERVER_NAME="testserver", ) self.assertEqual(response.status_code, 200) - self.assertContains( - response, - 'peer_review_data_block' - ) - + self.assertContains(response, "peer_review_data_block") def setUp(self): self.press = helpers.create_press() self.press.save() self.journal_one, self.journal_two = helpers.create_journals() - helpers.create_roles(["editor", "author", "reviewer", "proofreader", - "production", "copyeditor", "typesetter", - "proofing-manager", "section-editor", "reader"]) + helpers.create_roles( + [ + "editor", + "author", + "reviewer", + "proofreader", + "production", + "copyeditor", + "typesetter", + "proofing-manager", + "section-editor", + "reader", + ] + ) self.regular_user = helpers.create_user("regularuser@martineve.com") self.regular_user.is_active = True self.regular_user.save() - self.second_user = helpers.create_user("seconduser@martineve.com", ["reviewer"], journal=self.journal_one) + self.second_user = helpers.create_user( + "seconduser@martineve.com", ["reviewer"], journal=self.journal_one + ) self.second_user.is_active = True self.second_user.save() @@ -528,7 +546,7 @@ def setUp(self): self.admin_user.is_active = True self.admin_user.save() - call_command('install_plugins') + call_command("install_plugins") install.update_settings(management_command=False) self.article_one = helpers.create_article( self.journal_one, @@ -543,7 +561,7 @@ def setUp(self): ) self.article_two.stage = submission_models.STAGE_PUBLISHED self.article_two.date_published = timezone.now() - self.article_two.title = 'There is coffee in that nebula!' + self.article_two.title = "There is coffee in that nebula!" self.article_two.save() review_assignment = helpers.create_review_assignment( diff --git a/src/core/tests/test_files.py b/src/core/tests/test_files.py index 6bc34f5749..29d187a752 100644 --- a/src/core/tests/test_files.py +++ b/src/core/tests/test_files.py @@ -17,7 +17,6 @@ class TestFilesHandler(TestCase): - def setUp(self): self.press = helpers.create_press() self.press.save() @@ -28,7 +27,8 @@ def setUp(self): self.regular_user.save() self.article_in_production = submission_models.Article.objects.create( - owner=self.regular_user, title="A Test Article", + owner=self.regular_user, + title="A Test Article", abstract="An abstract", stage=submission_models.STAGE_TYPESETTING, journal=self.journal_one, @@ -63,8 +63,8 @@ def tearDown(self): os.unlink( os.path.join( settings.BASE_DIR, - 'files', - 'articles', + "files", + "articles", self.pk_string, file.uuid_filename, ) @@ -75,7 +75,10 @@ def test_save_file(self): expected_file_path = os.path.join( os.path.join( - settings.BASE_DIR, 'files', 'articles', self.pk_string, + settings.BASE_DIR, + "files", + "articles", + self.pk_string, ) ) @@ -99,19 +102,18 @@ def test_overwrite_file(self): file_path = os.path.join( settings.BASE_DIR, - 'files', - 'articles', + "files", + "articles", self.pk_string, file_to_replace.uuid_filename, ) - with open(file_path, 'rb') as file: + with open(file_path, "rb") as file: content = file.read() - self.assertEqual(content, b'content') + self.assertEqual(content, b"content") - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_serve_any_file(self): - file, path_parts = helpers.create_test_file( self, self.test_file_two, @@ -121,12 +123,12 @@ def test_serve_any_file(self): clear_script_prefix() url = reverse( - 'article_file_download', + "article_file_download", kwargs={ - 'identifier_type': 'id', - 'identifier': self.article_in_production.pk, - 'file_id': file.pk, - } + "identifier_type": "id", + "identifier": self.article_in_production.pk, + "file_id": file.pk, + }, ) self.client.force_login(self.regular_user) @@ -148,16 +150,16 @@ def test_pdf_to_text(self): with NamedTemporaryFile("r+b") as f: # Write a PDF File text = "Hello World" - config = pdfkit.configuration(wkhtmltopdf=os.path.join( - settings.BASE_DIR, - 'transform/cassius/bin/wkhtmltopdf' - )) + config = pdfkit.configuration( + wkhtmltopdf=os.path.join( + settings.BASE_DIR, "transform/cassius/bin/wkhtmltopdf" + ) + ) pdfkit.from_string(text, output_path=f.name, configuration=config) # Parse the PDF File parsed_text = files.pdf_to_text(f.name).strip() - self.assertEqual(parsed_text, text) def test_index_file(self): @@ -170,4 +172,3 @@ def test_index_file(self): indexed = file_.index_full_text() self.assertTrue(indexed) - diff --git a/src/core/tests/test_logic.py b/src/core/tests/test_logic.py index 591837b27a..bc1dbcaad9 100644 --- a/src/core/tests/test_logic.py +++ b/src/core/tests/test_logic.py @@ -4,6 +4,7 @@ from core.models import SettingGroup from utils.testing import helpers + class TestLogic(TestCase): def setUp(self): self.press = helpers.create_press() @@ -15,11 +16,11 @@ def setUp(self): self.request.site_type = self.journal_one def test_render_nested_settings(self): - expected_rendered_setting = "

For help with Janeway, contact --No support email set--.

" + expected_rendered_setting = '

For help with Janeway, contact --No support email set--.

' rendered_setting = logic.render_nested_setting( - 'support_contact_message_for_staff', - 'general', + "support_contact_message_for_staff", + "general", self.request, - nested_settings=[('support_email','general')], + nested_settings=[("support_email", "general")], ) self.assertEqual(expected_rendered_setting, rendered_setting) diff --git a/src/core/tests/test_middleware.py b/src/core/tests/test_middleware.py index 7cd30e15dd..87730b4b16 100644 --- a/src/core/tests/test_middleware.py +++ b/src/core/tests/test_middleware.py @@ -1,6 +1,7 @@ """ Unit tests for janeway core middleware """ + from django.contrib.auth.models import AnonymousUser from django.shortcuts import redirect from django.test import TestCase, override_settings @@ -8,11 +9,7 @@ from django.urls import reverse from django.core.management import call_command -from core.middleware import ( - SiteSettingsMiddleware, - TimezoneMiddleware, - BaseMiddleware -) +from core.middleware import SiteSettingsMiddleware, TimezoneMiddleware, BaseMiddleware from core.models import Account from journal.tests.utils import make_test_journal from press.models import Press @@ -20,12 +17,8 @@ class TestSiteMiddleware(TestCase): - def setUp(self): - journal_kwargs = dict( - code="test", - domain="journal.org" - ) + journal_kwargs = dict(code="test", domain="journal.org") press_kwargs = dict( domain="press.org", ) @@ -37,17 +30,17 @@ def setUp(self): @override_settings(URL_CONFIG="path") def test_journal_site_in_path_mode(self): - #expect - expected_path_info = ("/") + # expect + expected_path_info = "/" expected_journal = self.journal expected_press = self.press expected_site_type = expected_journal - #do + # do request = self.request_factory.get("http://press.org/test/") _response = self.middleware.process_request(request) - #assert + # assert self.assertEqual(expected_journal, request.journal) self.assertEqual(expected_press, request.press) self.assertEqual(expected_site_type, request.site_type) @@ -65,61 +58,61 @@ def test_journal_site_with_path_in_domain_mode(self): @override_settings(URL_CONFIG="path") def test_press_site_in_path_mode(self): - #expect - expected_path_info = ("/") + # expect + expected_path_info = "/" expected_journal = None expected_press = self.press expected_site_type = expected_press - #do + # do request = self.request_factory.get("/", SERVER_NAME="press.org") _response = self.middleware.process_request(request) - #assert + # assert self.assertEqual(expected_journal, request.journal) self.assertEqual(expected_press, request.press) self.assertEqual(expected_site_type, request.site_type) @override_settings(URL_CONFIG="domain") def test_journal_site_in_domain_mode(self): - #expect - expected_path_info = ("/") + # expect + expected_path_info = "/" expected_journal = self.journal expected_press = self.press expected_site_type = expected_journal - #do + # do request = self.request_factory.get("/", SERVER_NAME="journal.org") _response = self.middleware.process_request(request) - #assert + # assert self.assertEqual(expected_journal, request.journal) self.assertEqual(expected_press, request.press) self.assertEqual(expected_site_type, request.site_type) @override_settings(URL_CONFIG="domain") def test_press_site_in_domain_mode(self): - #expect - expected_path_info = ("/") + # expect + expected_path_info = "/" expected_journal = None expected_press = self.press expected_site_type = expected_press - #do + # do request = self.request_factory.get("/", SERVER_NAME="press.org") _response = self.middleware.process_request(request) - #assert + # assert self.assertEqual(expected_journal, request.journal) self.assertEqual(expected_press, request.press) self.assertEqual(expected_site_type, request.site_type) @override_settings(URL_CONFIG="path") def test_reverse_in_path_mode(self): - #expect + # expect expected_index_path = "/test/" - #do + # do request = self.request_factory.get("/test/", SERVER_NAME="press.org") _response = self.middleware.process_request(request) response = redirect(reverse("website_index")) @@ -133,12 +126,8 @@ def test_reverse_in_path_mode(self): class TestTimezoneMiddleware(TestCase): - def setUp(self): - journal_kwargs = dict( - code="test", - domain="journal.org" - ) + journal_kwargs = dict(code="test", domain="journal.org") press_kwargs = dict( domain="press.org", ) @@ -167,7 +156,7 @@ def test_default_case(self): def test_user_preference_case(self): request = self.request_factory.get("/test/", SERVER_NAME="press.org") request.session = {} - user = Account.objects.get(email='regularuser@timezone.com') + user = Account.objects.get(email="regularuser@timezone.com") user.preferred_timezone = "Europe/London" user.save() @@ -192,7 +181,7 @@ def test_user_preference_over_browser(self): user_timezone = "Europe/Madrid" browser_timezone = "Atlantic/Canary" - user = Account.objects.get(email='regularuser@timezone.com') + user = Account.objects.get(email="regularuser@timezone.com") user.preferred_timezone = user_timezone user.save() @@ -204,5 +193,3 @@ def test_user_preference_over_browser(self): response = self.middleware.process_request(request) self.assertEqual(request.timezone.zone, user_timezone) - - diff --git a/src/core/tests/test_models.py b/src/core/tests/test_models.py index e191a24c66..2172c4b3aa 100644 --- a/src/core/tests/test_models.py +++ b/src/core/tests/test_models.py @@ -26,33 +26,33 @@ class TestAccount(TestCase): def test_creation(self): data = { - 'email': 'test@test.com', - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "email": "test@test.com", + "is_active": True, + "password": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } models.Account.objects.create(**data) try: - models.Account.objects.get(email='test@test.com') + models.Account.objects.get(email="test@test.com") except models.Account.DoesNotExist: - self.fail('User account has not been created.') + self.fail("User account has not been created.") def test_username_normalised(self): email = "TEST@test.com" data = { - 'email': email, - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "email": email, + "is_active": True, + "password": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } obj = models.Account.objects.create(**data) self.assertEquals(obj.username, email.lower()) @@ -60,14 +60,14 @@ def test_username_normalised(self): def test_username_normalised_quick_form(self): email = "QUICK@test.com" data = { - 'email': email, - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "email": email, + "is_active": True, + "password": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } form = forms.QuickUserForm(data=data) acc = form.save() @@ -77,7 +77,7 @@ def test_email_normalised(self): email = "TEST@TEST.com" expected = "TEST@test.com" data = { - 'email': email, + "email": email, } obj = models.Account.objects.create(**data) self.assertEquals(obj.email, expected) @@ -187,12 +187,9 @@ def test_merge_accounts_o2m_unique(self): role=role_obj, ) unique_violation = models.AccountRole.objects.create( - user=to_account, - journal=journal, - role=role_obj + user=to_account, journal=journal, role=role_obj ) - # Test merge_models(from_account, to_account) @@ -204,12 +201,12 @@ def test_merge_accounts_o2m_unique(self): def test_full_name(self): author = models.Account.objects.create( - email='test@example.com', - first_name='', - middle_name='', - last_name='Sky', + email="test@example.com", + first_name="", + middle_name="", + last_name="Sky", ) - self.assertEqual('Sky', author.full_name()) + self.assertEqual("Sky", author.full_name()) class TestSVGImageFormField(TestCase): @@ -223,10 +220,7 @@ def test_upload_svg_to_svg_image_form_field(self): "file.svg", svg_data.encode("utf-8"), ) - TestForm = type( - "TestFormForm", (Form,), - {"file": SVGImageFieldForm()} - ) + TestForm = type("TestFormForm", (Form,), {"file": SVGImageFieldForm()}) form = TestForm({}, {"file": svg_file}) self.assertTrue(form.is_valid()) @@ -240,34 +234,27 @@ def test_upload_corrupt_svg_to_svg_image_form_field(self): "file.svg", svg_data.encode("utf-8"), ) - TestForm = type( - "TestFormForm", (Form,), - {"file": SVGImageFieldForm()} - ) + TestForm = type("TestFormForm", (Form,), {"file": SVGImageFieldForm()}) form = TestForm({}, {"file": svg_file}) self.assertFalse(form.is_valid()) def test_upload_image_to_svg_image_form_field(self): svg_data = "" image_data = ( - b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04' - b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02' - b'\x02\x4c\x01\x00\x3b' + b"\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04" + b"\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02" + b"\x02\x4c\x01\x00\x3b" ) image_file = SimpleUploadedFile( "file.gif", image_data, ) - TestForm = type( - "TestFormForm", (Form,), - {"file": SVGImageFieldForm()} - ) + TestForm = type("TestFormForm", (Form,), {"file": SVGImageFieldForm()}) form = TestForm({}, {"file": image_file}) self.assertTrue(form.is_valid()) class TestLastModifiedModel(TestCase): - def setUp(self): self.press = helpers.create_press() self.press.save() @@ -275,39 +262,30 @@ def setUp(self): self.issue = helpers.create_issue(self.journal_one) self.article, c = submission_models.Article.objects.get_or_create( - title='Test Model Utils Article', + title="Test Model Utils Article", ) def test_abstract_last_mod_save(self): - test_abstract_text = 'The Phantom Menace Sucks' + test_abstract_text = "The Phantom Menace Sucks" self.article.abstract = test_abstract_text self.article.save() - self.assertEqual( - self.article.abstract, - test_abstract_text - ) + self.assertEqual(self.article.abstract, test_abstract_text) def test_abstract_last_mod_update_doesnt_die(self): article_last_mod = self.article.last_modified - articles = submission_models.Article.objects.filter( - pk=self.article.pk - ).update( - title='You\'re Wrong About the Phantom Menace' + articles = submission_models.Article.objects.filter(pk=self.article.pk).update( + title="You're Wrong About the Phantom Menace" ) def test_last_modified_model(self): # prepare with freeze_time(FROZEN_DATETIME_20210102): - issue_date = self.issue.last_modified = ( - timezone.now() - timedelta(days=1) - ) + issue_date = self.issue.last_modified = timezone.now() - timedelta(days=1) self.issue.save() with freeze_time(FROZEN_DATETIME_20210101): - self.article.last_modified = ( - timezone.now() - timedelta(days=2) - ) + self.article.last_modified = timezone.now() - timedelta(days=2) self.article.save() # Test @@ -379,16 +357,15 @@ def test_last_modified_model_recursive_doubly_linked(self): class TestModelUtils(TestCase): - @classmethod def setUpTestData(cls): cls.account = helpers.create_user( - 'Ab6CrWPPxQ7FoLj5dgdH@example.org', + "Ab6CrWPPxQ7FoLj5dgdH@example.org", ) def test_search_model_admin(self): request = HttpRequest() - request.GET = QueryDict('q=Ab6CrWPPxQ7FoLj5dgdH') + request.GET = QueryDict("q=Ab6CrWPPxQ7FoLj5dgdH") results, _duplicates = search_model_admin( request, models.Account, diff --git a/src/core/tests/test_plugins.py b/src/core/tests/test_plugins.py index bfc0c1d67a..d8cd7f0bca 100644 --- a/src/core/tests/test_plugins.py +++ b/src/core/tests/test_plugins.py @@ -9,23 +9,19 @@ MockSettings = namedtuple("MockSettings", ["JANEWAY_VERSION", "PLUGIN_NAME"]) MockSettingsNoVersion = namedtuple("MockSettings", ["PLUGIN_NAME"]) -class TestPluginLoader(TestCase): +class TestPluginLoader(TestCase): def setUp(self): Version.objects.create(number="9.6.3") def test_plugin_version_is_valid(self): - mock_settings = MockSettings( - PLUGIN_NAME="Mock Plugin", - JANEWAY_VERSION="1.3.6" - ) + mock_settings = MockSettings(PLUGIN_NAME="Mock Plugin", JANEWAY_VERSION="1.3.6") plugin_loader.validate_plugin_version(mock_settings) def test_plugin_version_is_not_valid(self): mock_settings = MockSettings( - PLUGIN_NAME="Mock Plugin", - JANEWAY_VERSION="10.7.2" + PLUGIN_NAME="Mock Plugin", JANEWAY_VERSION="10.7.2" ) with self.assertRaises(ImproperlyConfigured): @@ -33,8 +29,7 @@ def test_plugin_version_is_not_valid(self): def test_plugin_minor_version_is_not_valid(self): mock_settings = MockSettings( - PLUGIN_NAME="Mock Plugin", - JANEWAY_VERSION="10.6.8" + PLUGIN_NAME="Mock Plugin", JANEWAY_VERSION="10.6.8" ) with self.assertRaises(ImproperlyConfigured): diff --git a/src/core/tests/test_settings.py b/src/core/tests/test_settings.py index 044710e614..d55088d4e9 100644 --- a/src/core/tests/test_settings.py +++ b/src/core/tests/test_settings.py @@ -7,7 +7,6 @@ class TestSettingHandler(TestCase): - def setUp(self): self.press = helpers.create_press() self.press.save() @@ -18,19 +17,22 @@ def test_set_and_get_journal_setting(self): setting_name = "test_get_setting" setting_value = "banana" setting = setting_handler.create_setting( - "test_group", "test_get_setting", - type="text", - pretty_name="Some Name", - description=None, + "test_group", + "test_get_setting", + type="text", + pretty_name="Some Name", + description=None, ) setting_handler.save_setting( - "test_group", "test_get_setting", - journal=self.journal_one, - value=setting_value, + "test_group", + "test_get_setting", + journal=self.journal_one, + value=setting_value, ) result = setting_handler.get_setting( - "test_group", "test_get_setting", - journal=self.journal_one, + "test_group", + "test_get_setting", + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -38,21 +40,24 @@ def test_get_journal_setting_with_lang_fallback(self): setting_name = "test_fallback_setting" setting_value = "banana" setting = setting_handler.create_setting( - "test_group", "test_get_setting", - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + "test_get_setting", + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) setting_handler.save_setting( - "test_group", "test_get_setting", - journal=self.journal_one, - value=setting_value + "test_group", + "test_get_setting", + journal=self.journal_one, + value=setting_value, ) with helpers.activate_translation("cy"): result = setting_handler.get_setting( - "test_group", "test_get_setting", - journal=self.journal_one, + "test_group", + "test_get_setting", + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -60,20 +65,23 @@ def test_get_journal_setting_with_default_fallback(self): setting_name = "test_get_default_setting" setting_value = "default_banana" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=False, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=False, ) setting_handler.save_setting( - "test_group", setting_name, - journal=None, - value=setting_value, + "test_group", + setting_name, + journal=None, + value=setting_value, ) result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -81,21 +89,21 @@ def test_get_journal_setting_with_lang_and_default_fallback(self): setting_name = "test_get_default_fallback_for_lang_setting" setting_value = "banana" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) setting_handler.save_setting( - "test_group", setting_name, - journal=None, - value=setting_value + "test_group", setting_name, journal=None, value=setting_value ) with helpers.activate_translation("cy"): result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -104,20 +112,23 @@ def test_get_journal_setting_with_language(self): setting_name = "test_get_setting_with_language" setting_value = "plátano" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=setting_value, ) result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -125,20 +136,23 @@ def test_save_setting(self): setting_name = "test_save_setting" setting_value = "This is the setting" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=setting_value, ) result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ) self.assertEqual(result.value, setting_value) @@ -149,21 +163,24 @@ def test_save_translated_setting_without_default_lang(self): setting_value = "plátano" expected_result = None setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=setting_value, ) with helpers.activate_translation(settings.LANGUAGE_CODE): result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ) self.assertEqual(result.value, expected_result) @@ -173,33 +190,38 @@ def test_save_translated_setting_with_default_lang(self): setting_value = "banana" xl_setting_value = "plátano" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) - #Save the setting on the default language + # Save the setting on the default language setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=setting_value, ) - #Save the translated value + # Save the translated value with helpers.activate_translation("cy"): setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=xl_setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=xl_setting_value, ) result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ).value with helpers.activate_translation("cy"): xl_result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ).value self.assertEqual(result, setting_value) @@ -211,42 +233,48 @@ def test_update_translated_setting_with_default_lang(self): setting_value = "banana" xl_setting_value = "plátano" setting = setting_handler.create_setting( - "test_group", setting_name, - type="text", - pretty_name="Pretty Name", - description=None, - is_translatable=True, + "test_group", + setting_name, + type="text", + pretty_name="Pretty Name", + description=None, + is_translatable=True, ) - #Save the setting on the default language + # Save the setting on the default language setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=setting_value, ) - #Save a wrongly translated value + # Save a wrongly translated value wrong_translation = xl_setting_value + "mal" with helpers.activate_translation("cy"): setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=wrong_translation, + "test_group", + setting_name, + journal=self.journal_one, + value=wrong_translation, ) - #save the correctly translated value + # save the correctly translated value setting_handler.save_setting( - "test_group", setting_name, - journal=self.journal_one, - value=xl_setting_value, + "test_group", + setting_name, + journal=self.journal_one, + value=xl_setting_value, ) - #Fetch the results + # Fetch the results result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ).value with helpers.activate_translation("cy"): xl_result = setting_handler.get_setting( - "test_group", setting_name, - journal=self.journal_one, + "test_group", + setting_name, + journal=self.journal_one, ).value self.assertEqual(result, setting_value) diff --git a/src/core/tests/test_validators.py b/src/core/tests/test_validators.py index 8a7fd6b444..996d03542a 100644 --- a/src/core/tests/test_validators.py +++ b/src/core/tests/test_validators.py @@ -3,8 +3,8 @@ from django.core.exceptions import ValidationError from core import models, validators -class TestValidators(TestCase): +class TestValidators(TestCase): def test_valid_file(self): valid_extensions = {".gz"} valid_mimetypes = {"application/x-tar"} @@ -31,14 +31,12 @@ def test_invalid_file_extension(self): with self.assertRaises(ValidationError): validator(file_) - def test_invalid_mime_type(self): valid_extensions = {".gz"} valid_mimetypes = {"application/x-tar"} validator = validators.FileTypeValidator(mimetypes=valid_mimetypes) file_ = SimpleUploadedFile("test.gz", bytes()) - with self.assertRaises(ValidationError): validator(file_) @@ -56,4 +54,3 @@ def test_valid_email_setting(self): else: error = None self.assertIsNone(error) - diff --git a/src/core/translation.py b/src/core/translation.py index f142aff61f..6fa46f8f7f 100644 --- a/src/core/translation.py +++ b/src/core/translation.py @@ -5,14 +5,20 @@ @register(models.SettingValue) class SettingValueTranslationOptions(TranslationOptions): - fields = ('value',) + fields = ("value",) @register(models.EditorialGroup) class EditorialGroupTranslationOptions(TranslationOptions): - fields = ('name', 'description',) + fields = ( + "name", + "description", + ) @register(models.Contacts) class ContactTranslationOptions(TranslationOptions): - fields = ('name', 'role',) + fields = ( + "name", + "role", + ) diff --git a/src/core/urls.py b/src/core/urls.py index c441918cde..1d42e5008e 100755 --- a/src/core/urls.py +++ b/src/core/urls.py @@ -16,13 +16,13 @@ logger = get_logger(__name__) -include('events.registration') +include("events.registration") urlpatterns = [ - path('', press_views.index, name='website_index'), - path('admin/', admin.site.urls), - path('summernote/', include('django_summernote.urls')), - path('', include('core.include_urls')), + path("", press_views.index, name="website_index"), + path("admin/", admin.site.urls), + path("summernote/", include("django_summernote.urls")), + path("", include("core.include_urls")), ] try: @@ -30,18 +30,20 @@ import debug_toolbar urlpatterns += [ - re_path(r'^media/(?P.*)$', serve, {'document_root': settings.MEDIA_ROOT}), - re_path(r'^404/$', error_views.handler404), - re_path(r'^500/$', error_views.handler500), - path('__debug__/', include('debug_toolbar.urls')), + re_path( + r"^media/(?P.*)$", serve, {"document_root": settings.MEDIA_ROOT} + ), + re_path(r"^404/$", error_views.handler404), + re_path(r"^500/$", error_views.handler500), + path("__debug__/", include("debug_toolbar.urls")), ] try: urlpatterns += [ - path('__reload__/', include('django_browser_reload.urls')), + path("__reload__/", include("django_browser_reload.urls")), ] except ModuleNotFoundError: - logger.debug('django_browser_reload is not set up') + logger.debug("django_browser_reload is not set up") except AttributeError: pass @@ -50,10 +52,10 @@ if settings.HIJACK_USERS_ENABLED: try: urlpatterns += [ - re_path(r'^control_user/', include('hijack.urls', namespace='hijack')), + re_path(r"^control_user/", include("hijack.urls", namespace="hijack")), ] except AttributeError: - logger.warning('Could not import Hijack URLs.') + logger.warning("Could not import Hijack URLs.") -handler404 = 'core.error_views.handler404' -handler500 = 'core.error_views.handler500' +handler404 = "core.error_views.handler404" +handler500 = "core.error_views.handler500" diff --git a/src/core/validators.py b/src/core/validators.py index dfcfed4d68..b0af1353bc 100644 --- a/src/core/validators.py +++ b/src/core/validators.py @@ -6,16 +6,22 @@ from django.template.exceptions import TemplateSyntaxError from django.utils.translation import gettext_lazy as _ + class FileTypeValidator(object): - """ Validates file against given lists of extensions and mimetypes + """Validates file against given lists of extensions and mimetypes :param extensions: iterable object (ideally a set) :param mimetypes: iterable object (ideally a set) """ + error_messages = { - "ext": _("Extension {extension} is not allowed. " - "Allowed extensions are: {validator.extensions}"), - "mime": _("MIME type {mimetype} is not valid. " - "Valid types are: {validator.mimetypes}"), + "ext": _( + "Extension {extension} is not allowed. " + "Allowed extensions are: {validator.extensions}" + ), + "mime": _( + "MIME type {mimetype} is not valid. " + "Valid types are: {validator.mimetypes}" + ), } def __init__(self, extensions=None, mimetypes=None): diff --git a/src/core/views.py b/src/core/views.py index 2b3812f829..a4d8f993ca 100755 --- a/src/core/views.py +++ b/src/core/views.py @@ -36,9 +36,12 @@ from core import models, forms, logic, workflow, files, models as core_models from core.model_utils import NotImplementedField, search_model_admin from security.decorators import ( - editor_user_required, article_author_required, has_journal, - any_editor_user_required, role_can_access, - user_can_edit_setting + editor_user_required, + article_author_required, + has_journal, + any_editor_user_required, + role_can_access, + user_can_edit_setting, ) from submission import models as submission_models from review import models as review_models @@ -66,21 +69,21 @@ def user_login(request): :return: HttpResponse """ if request.user.is_authenticated: - messages.info(request, 'You are already logged in.') - if request.GET.get('next'): - return redirect(request.GET.get('next')) + messages.info(request, "You are already logged in.") + if request.GET.get("next"): + return redirect(request.GET.get("next")) else: - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) else: bad_logins = logic.check_for_bad_login_attempts(request) if bad_logins >= 10: messages.info( request, - _('You have been banned from logging in due to failed attempts.'), + _("You have been banned from logging in due to failed attempts."), ) logger.warning("[LOGIN_DENIED][FAILURES:%d]" % bad_logins) - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) form = forms.LoginForm(bad_logins=bad_logins) @@ -88,94 +91,103 @@ def user_login(request): form = forms.LoginForm(request.POST, bad_logins=bad_logins) if form.is_valid(): - user = request.POST.get('user_name').lower() - pawd = request.POST.get('user_pass') + user = request.POST.get("user_name").lower() + pawd = request.POST.get("user_pass") user = authenticate(username=user, password=pawd) if user is not None: login(request, user) - messages.info(request, 'Login successful.') + messages.info(request, "Login successful.") logic.clear_bad_login_attempts(request) - orcid_token = request.POST.get('orcid_token', None) + orcid_token = request.POST.get("orcid_token", None) if orcid_token: try: - token_obj = models.OrcidToken.objects.get(token=orcid_token, expiry__gt=timezone.now()) + token_obj = models.OrcidToken.objects.get( + token=orcid_token, expiry__gt=timezone.now() + ) user.orcid = token_obj.orcid user.save() token_obj.delete() except models.OrcidToken.DoesNotExist: pass - if request.GET.get('next'): - return redirect(request.GET.get('next')) + if request.GET.get("next"): + return redirect(request.GET.get("next")) elif request.journal: - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) else: - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) else: - - empty_password_check = logic.no_password_check(request.POST.get('user_name').lower()) + empty_password_check = logic.no_password_check( + request.POST.get("user_name").lower() + ) if empty_password_check: - messages.add_message(request, messages.INFO, + messages.add_message( + request, + messages.INFO, _( - 'Password reset process has been initiated,' - ' please check your inbox for a' - ' reset request link.' + "Password reset process has been initiated," + " please check your inbox for a" + " reset request link." ), ) logic.start_reset_process(request, empty_password_check) else: - messages.add_message( - request, messages.ERROR, - _('Wrong email/password combination or your' - ' email address has not been confirmed yet.'), + request, + messages.ERROR, + _( + "Wrong email/password combination or your" + " email address has not been confirmed yet." + ), + ) + util_models.LogEntry.add_entry( + types="Authentication", + description="Failed login attempt for user {0}".format( + request.POST.get("user_name") + ), + level="Info", + actor=None, + request=request, ) - util_models.LogEntry.add_entry(types='Authentication', - description='Failed login attempt for user {0}'.format( - request.POST.get('user_name')), - level='Info', actor=None, request=request) logic.add_failed_login_attempt(request) context = { - 'form': form, + "form": form, } - template = 'core/login.html' + template = "core/login.html" return render(request, template, context) + def user_login_orcid(request): """ Allows a user to login with ORCiD :param request: HttpRequest object :return: HttpResponse object """ - orcid_code = request.GET.get('code', None) - action = request.GET.get('state', 'login') + orcid_code = request.GET.get("code", None) + action = request.GET.get("state", "login") if orcid_code and django_settings.ENABLE_ORCID: - orcid_id = orcid.retrieve_tokens( - orcid_code, - request.site_type, - action=action - ) + orcid_id = orcid.retrieve_tokens(orcid_code, request.site_type, action=action) if orcid_id: try: user = models.Account.objects.get(orcid=orcid_id) - if action == 'login': - user.backend = 'django.contrib.auth.backends.ModelBackend' + if action == "login": + user.backend = "django.contrib.auth.backends.ModelBackend" login(request, user) - if request.GET.get('next'): - return redirect(request.GET.get('next')) + if request.GET.get("next"): + return redirect(request.GET.get("next")) elif request.journal: - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) else: - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) except models.Account.DoesNotExist: # Lookup ORCID email addresses @@ -186,34 +198,39 @@ def user_login_orcid(request): # Store ORCID for future authentication requests candidates.update(orcid=orcid_id) login(request, candidates.first()) - if request.GET.get('next'): - return redirect(request.GET.get('next')) + if request.GET.get("next"): + return redirect(request.GET.get("next")) elif request.journal: - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) else: - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) # Prepare ORCID Token for registration and redirect models.OrcidToken.objects.filter(orcid=orcid_id).delete() new_token = models.OrcidToken.objects.create(orcid=orcid_id) - if action == 'register': - return redirect(reverse('core_register') + f'?token={new_token.token}') + if action == "register": + return redirect(reverse("core_register") + f"?token={new_token.token}") else: - return redirect(reverse('core_orcid_registration', kwargs={'token': new_token.token})) + return redirect( + reverse( + "core_orcid_registration", kwargs={"token": new_token.token} + ) + ) else: messages.add_message( request, messages.WARNING, - 'Valid ORCiD not returned, please try again, or login with your username and password.' + "Valid ORCiD not returned, please try again, or login with your username and password.", ) - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) else: messages.add_message( request, messages.WARNING, - 'No authorisation code provided, please try again or login with your username and password.') - return redirect(reverse('core_login')) + "No authorisation code provided, please try again or login with your username and password.", + ) + return redirect(reverse("core_login")) @login_required @@ -223,9 +240,9 @@ def user_logout(request): :param request: HttpRequest object :return: HttpResponse object """ - messages.info(request, _('You have been logged out.')) + messages.info(request, _("You have been logged out.")) logout(request) - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) def get_reset_token(request): @@ -244,21 +261,21 @@ def get_reset_token(request): if form.is_valid(): email_address = form.cleaned_data.get("email_address") messages.add_message( - request, + request, messages.INFO, - _('If your account was found, an email has been sent to you.'), - ) + _("If your account was found, an email has been sent to you."), + ) try: account = models.Account.objects.get(email__iexact=email_address) logic.start_reset_process(request, account) - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) except models.Account.DoesNotExist: - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) - template = 'core/accounts/get_reset_token.html' + template = "core/accounts/get_reset_token.html" context = { - 'new_reset_token': new_reset_token, - 'form': form, + "new_reset_token": new_reset_token, + "form": form, } return render(request, template, context) @@ -271,7 +288,9 @@ def reset_password(request, token): :param token: string, PasswordResetToken.token :return: HttpResponse object """ - reset_token = get_object_or_404(models.PasswordResetToken, token=token, expired=False) + reset_token = get_object_or_404( + models.PasswordResetToken, token=token, expired=False + ) form = forms.PasswordResetForm() if reset_token.has_expired(): @@ -284,23 +303,25 @@ def reset_password(request, token): if password_policy_check: for policy_fail in password_policy_check: - form.add_error('password_1', policy_fail) + form.add_error("password_1", policy_fail) if form.is_valid(): - password = form.cleaned_data['password_2'] + password = form.cleaned_data["password_2"] reset_token.account.set_password(password) reset_token.account.is_active = True logic.clear_bad_login_attempts(request) reset_token.account.save() reset_token.expired = True reset_token.save() - messages.add_message(request, messages.SUCCESS, 'Your password has been reset.') - return redirect(reverse('core_login')) + messages.add_message( + request, messages.SUCCESS, "Your password has been reset." + ) + return redirect(reverse("core_login")) - template = 'core/accounts/reset_password.html' + template = "core/accounts/reset_password.html" context = { - 'reset_token': reset_token, - 'form': form, + "reset_token": reset_token, + "form": form, } return render(request, template, context) @@ -315,7 +336,7 @@ def register(request): """ context = {} initial = {} - token, token_obj = request.GET.get('token', None), None + token, token_obj = request.GET.get("token", None), None if token: token_obj = get_object_or_404(models.OrcidToken, token=token) orcid_details = orcid.get_orcid_record_details(token_obj.orcid) @@ -328,10 +349,12 @@ def register(request): if orcid_details.get("emails"): initial["email"] = orcid_details["emails"][0] if orcid_details.get("affiliation"): - initial['institution'] = orcid_details['affiliation'] + initial["institution"] = orcid_details["affiliation"] if orcid_details.get("country"): - if models.Country.objects.filter(code=orcid_details['country']).exists(): - initial["country"] = models.Country.objects.get(code=orcid_details['country']) + if models.Country.objects.filter(code=orcid_details["country"]).exists(): + initial["country"] = models.Country.objects.get( + code=orcid_details["country"] + ) form = forms.RegistrationForm( journal=request.journal, @@ -348,7 +371,7 @@ def register(request): if password_policy_check: for policy_fail in password_policy_check: - form.add_error('password_1', policy_fail) + form.add_error("password_1", policy_fail) if form.is_valid(): if token_obj: @@ -359,27 +382,30 @@ def register(request): new_user.is_active = True new_user.save() login(request, new_user) - if request.GET.get('next'): - return redirect(request.GET.get('next')) + if request.GET.get("next"): + return redirect(request.GET.get("next")) elif request.journal: - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) else: - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) else: new_user = form.save() if request.journal: - new_user.add_account_role('author', request.journal) + new_user.add_account_role("author", request.journal) logic.send_confirmation_link(request, new_user) messages.add_message( - request, messages.SUCCESS, - _('Your account has been created, please follow the' - 'instructions in the email that has been sent to you.'), + request, + messages.SUCCESS, + _( + "Your account has been created, please follow the" + "instructions in the email that has been sent to you." + ), ) - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) - template = 'core/accounts/register.html' + template = "core/accounts/register.html" context["form"] = form return render(request, template, context) @@ -388,9 +414,9 @@ def register(request): def orcid_registration(request, token): token = get_object_or_404(models.OrcidToken, token=token, expiry__gt=timezone.now()) - template = 'core/accounts/orcid_registration.html' + template = "core/accounts/orcid_registration.html" context = { - 'token': token, + "token": token, } return render(request, template, context) @@ -417,14 +443,14 @@ def activate_account(request, token): messages.add_message( request, messages.SUCCESS, - _('Account activated'), + _("Account activated"), ) - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) - template = 'core/accounts/activate_account.html' + template = "core/accounts/activate_account.html" context = { - 'account': account, + "account": account, } return render(request, template, context) @@ -442,9 +468,7 @@ def edit_profile(request): send_reader_notifications = False if request.journal: send_reader_notifications = setting_handler.get_setting( - 'notifications', - 'send_reader_notifications', - request.journal + "notifications", "send_reader_notifications", request.journal ).value if user.staffgroupmember_set.first(): @@ -455,97 +479,103 @@ def edit_profile(request): staff_group_membership_form = None if request.POST: - if 'email' in request.POST: - email_address = request.POST.get('email_address') + if "email" in request.POST: + email_address = request.POST.get("email_address") try: validate_email(email_address) try: logic.handle_email_change(request, email_address) - return redirect(reverse('website_index')) + return redirect(reverse("website_index")) except IntegrityError: messages.add_message( request, messages.WARNING, - _('An account with that email address already exists.'), + _("An account with that email address already exists."), ) except ValidationError: messages.add_message( request, messages.WARNING, - _('Email address is not valid.'), + _("Email address is not valid."), ) - elif 'change_password' in request.POST: - old_password = request.POST.get('current_password') - new_pass_one = request.POST.get('new_password_one') - new_pass_two = request.POST.get('new_password_two') + elif "change_password" in request.POST: + old_password = request.POST.get("current_password") + new_pass_one = request.POST.get("new_password_one") + new_pass_two = request.POST.get("new_password_two") if old_password and request.user.check_password(old_password): - if new_pass_one == new_pass_two: problems = request.user.password_policy_check(request, new_pass_one) if not problems: request.user.set_password(new_pass_one) request.user.save() - messages.add_message(request, messages.SUCCESS, _('Password updated.')) + messages.add_message( + request, messages.SUCCESS, _("Password updated.") + ) else: - [messages.add_message(request, messages.INFO, problem) for problem in problems] + [ + messages.add_message(request, messages.INFO, problem) + for problem in problems + ] else: - messages.add_message(request, messages.WARNING, _('Passwords do not match')) + messages.add_message( + request, messages.WARNING, _("Passwords do not match") + ) else: - messages.add_message(request, messages.WARNING, _('Old password is not correct.')) + messages.add_message( + request, messages.WARNING, _("Old password is not correct.") + ) - elif 'subscribe' in request.POST and send_reader_notifications: + elif "subscribe" in request.POST and send_reader_notifications: request.user.add_account_role( - 'reader', + "reader", request.journal, ) messages.add_message( request, messages.SUCCESS, - _('Successfully subscribed to article notifications.'), + _("Successfully subscribed to article notifications."), ) - elif 'unsubscribe' in request.POST and send_reader_notifications: - request.user.remove_account_role( - 'reader', - request.journal - ) + elif "unsubscribe" in request.POST and send_reader_notifications: + request.user.remove_account_role("reader", request.journal) messages.add_message( request, messages.SUCCESS, - _('Successfully unsubscribed from article notifications.'), + _("Successfully unsubscribed from article notifications."), ) - elif 'edit_profile' in request.POST: + elif "edit_profile" in request.POST: form = forms.EditAccountForm(request.POST, request.FILES, instance=user) if form.is_valid(): form.save() - messages.add_message(request, messages.SUCCESS, 'Profile updated.') - return redirect(reverse('core_edit_profile')) + messages.add_message(request, messages.SUCCESS, "Profile updated.") + return redirect(reverse("core_edit_profile")) - elif 'edit_staff_member_info' in request.POST: + elif "edit_staff_member_info" in request.POST: form = press_forms.StaffGroupMemberForm( - request.POST, - instance=user.staffgroupmember_set.first() + request.POST, instance=user.staffgroupmember_set.first() ) if form.is_valid(): form.save() - messages.add_message(request, messages.SUCCESS, 'Staff member info updated.') - return redirect(reverse('core_edit_profile')) + messages.add_message( + request, messages.SUCCESS, "Staff member info updated." + ) + return redirect(reverse("core_edit_profile")) - elif 'export' in request.POST: + elif "export" in request.POST: return logic.export_gdpr_user_profile(user) - template = 'core/accounts/edit_profile.html' + template = "core/accounts/edit_profile.html" context = { - 'form': form, - 'staff_group_membership_form': staff_group_membership_form, - 'user_to_edit': user, - 'send_reader_notifications': send_reader_notifications, + "form": form, + "staff_group_membership_form": staff_group_membership_form, + "user_to_edit": user, + "send_reader_notifications": send_reader_notifications, } return render(request, template, context) @@ -565,28 +595,28 @@ def public_profile(request, uuid): is_active=True, enable_public_profile=True, ) - template = 'core/accounts/public_profile.html' + template = "core/accounts/public_profile.html" context = { - 'user': user, + "user": user, } if request.journal: - context['editorial_groups'] = user.editorialgroupmember_set.filter( + context["editorial_groups"] = user.editorialgroupmember_set.filter( group__journal=request.journal ) - context['roles'] = models.AccountRole.objects.filter( + context["roles"] = models.AccountRole.objects.filter( user=user, journal=request.journal, ) - if not context['roles']: + if not context["roles"]: raise Http404() elif request.press: - context['editorial_groups'] = user.editorialgroupmember_set.filter( + context["editorial_groups"] = user.editorialgroupmember_set.filter( group__press=request.press, group__journal__isnull=True, ) - context['staff_groups'] = user.staffgroupmember_set.all() + context["staff_groups"] = user.staffgroupmember_set.all() return render(request, template, context) @@ -599,111 +629,161 @@ def dashboard(request): :param request: HttpRequest object :return: HttpResponse object """ - template = 'core/dashboard.html' - new_proofing, active_proofing, completed_proofing = proofing_logic.get_tasks(request) - new_proofing_typesetting, active_proofing_typesetting, completed_proofing_typesetting = proofing_logic.get_typesetting_tasks(request) - section_editor_articles = review_models.EditorAssignment.objects.filter(editor=request.user, - editor_type='section-editor', - article__journal=request.journal) + template = "core/dashboard.html" + new_proofing, active_proofing, completed_proofing = proofing_logic.get_tasks( + request + ) + ( + new_proofing_typesetting, + active_proofing_typesetting, + completed_proofing_typesetting, + ) = proofing_logic.get_typesetting_tasks(request) + section_editor_articles = review_models.EditorAssignment.objects.filter( + editor=request.user, + editor_type="section-editor", + article__journal=request.journal, + ) # TODO: Move most of this to model logic. context = { - 'new_proofing': new_proofing.count(), - 'active_proofing': active_proofing.count(), - 'completed_proofing': completed_proofing.count(), - 'new_proofing_typesetting': new_proofing_typesetting.count(), - 'completed_proofing_typesetting': completed_proofing_typesetting.count(), - 'active_proofing_typesetting': active_proofing_typesetting.count(), - 'unassigned_articles_count': submission_models.Article.objects.filter( - stage=submission_models.STAGE_UNASSIGNED, journal=request.journal).count(), - 'assigned_articles_count': submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_ASSIGNED) | Q(stage=submission_models.STAGE_UNDER_REVIEW) | Q( - stage=submission_models.STAGE_UNDER_REVISION), journal=request.journal).count(), - 'editing_articles_count': submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_EDITOR_COPYEDITING) | Q( - stage=submission_models.STAGE_AUTHOR_COPYEDITING) | Q( - stage=submission_models.STAGE_FINAL_COPYEDITING), journal=request.journal).count(), - 'production_articles_count': submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_TYPESETTING), journal=request.journal).count(), - 'proofing_articles_count': submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_PROOFING), journal=request.journal).count(), - 'prepub_articles_count': submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION), journal=request.journal).count(), - 'is_editor': request.user.is_editor(request), - 'is_author': request.user.is_author(request), - 'is_reviewer': request.user.is_reviewer(request), - 'section_editor_articles': section_editor_articles, - 'active_submission_count': submission_models.Article.objects.filter( + "new_proofing": new_proofing.count(), + "active_proofing": active_proofing.count(), + "completed_proofing": completed_proofing.count(), + "new_proofing_typesetting": new_proofing_typesetting.count(), + "completed_proofing_typesetting": completed_proofing_typesetting.count(), + "active_proofing_typesetting": active_proofing_typesetting.count(), + "unassigned_articles_count": submission_models.Article.objects.filter( + stage=submission_models.STAGE_UNASSIGNED, journal=request.journal + ).count(), + "assigned_articles_count": submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_ASSIGNED) + | Q(stage=submission_models.STAGE_UNDER_REVIEW) + | Q(stage=submission_models.STAGE_UNDER_REVISION), + journal=request.journal, + ).count(), + "editing_articles_count": submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_EDITOR_COPYEDITING) + | Q(stage=submission_models.STAGE_AUTHOR_COPYEDITING) + | Q(stage=submission_models.STAGE_FINAL_COPYEDITING), + journal=request.journal, + ).count(), + "production_articles_count": submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_TYPESETTING), journal=request.journal + ).count(), + "proofing_articles_count": submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_PROOFING), journal=request.journal + ).count(), + "prepub_articles_count": submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION), + journal=request.journal, + ).count(), + "is_editor": request.user.is_editor(request), + "is_author": request.user.is_author(request), + "is_reviewer": request.user.is_reviewer(request), + "section_editor_articles": section_editor_articles, + "active_submission_count": submission_models.Article.objects.filter( + owner=request.user, journal=request.journal + ) + .exclude(stage=submission_models.STAGE_UNSUBMITTED) + .count(), + "in_progress_submission_count": submission_models.Article.objects.filter( owner=request.user, - journal=request.journal).exclude( - stage=submission_models.STAGE_UNSUBMITTED).count(), - 'in_progress_submission_count': submission_models.Article.objects.filter(owner=request.user, - journal=request.journal, - stage=submission_models. - STAGE_UNSUBMITTED).count(), - 'assigned_articles_for_user_review_count': review_models.ReviewAssignment.objects.filter( - Q(is_complete=False) & - Q(reviewer=request.user) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(date_accepted__isnull=True), article__journal=request.journal).count(), - 'assigned_articles_for_user_review_accepted_count': review_models.ReviewAssignment.objects.filter( - Q(is_complete=False) & - Q(reviewer=request.user) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(date_accepted__isnull=False), article__journal=request.journal).count(), - 'assigned_articles_for_user_review_completed_count': review_models.ReviewAssignment.objects.filter( - Q(is_complete=True) & - Q(reviewer=request.user) & - Q(date_declined__isnull=True), article__journal=request.journal).count(), - - 'copyeditor_requests': copyedit_models.CopyeditAssignment.objects.filter( - Q(copyeditor=request.user) & - Q(decision__isnull=True) & - Q(copyedit_reopened__isnull=True), article__journal=request.journal).count(), - 'copyeditor_accepted_requests': copyedit_models.CopyeditAssignment.objects.filter( - Q(copyeditor=request.user, decision='accept', copyeditor_completed__isnull=True, - article__journal=request.journal) | - Q(copyeditor=request.user, decision='accept', copyeditor_completed__isnull=False, - article__journal=request.journal, copyedit_reopened__isnull=False, - copyedit_reopened_complete__isnull=True) + journal=request.journal, + stage=submission_models.STAGE_UNSUBMITTED, ).count(), - 'copyeditor_completed_requests': copyedit_models.CopyeditAssignment.objects.filter( - (Q(copyeditor=request.user) & Q(copyeditor_completed__isnull=False)) | - (Q(copyeditor=request.user) & Q(copyeditor_completed__isnull=False) & - Q(copyedit_reopened_complete__isnull=False)), article__journal=request.journal).count(), - - 'typeset_tasks': production_models.TypesetTask.active_objects.filter( + "assigned_articles_for_user_review_count": review_models.ReviewAssignment.objects.filter( + Q(is_complete=False) + & Q(reviewer=request.user) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(date_accepted__isnull=True), + article__journal=request.journal, + ).count(), + "assigned_articles_for_user_review_accepted_count": review_models.ReviewAssignment.objects.filter( + Q(is_complete=False) + & Q(reviewer=request.user) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(date_accepted__isnull=False), + article__journal=request.journal, + ).count(), + "assigned_articles_for_user_review_completed_count": review_models.ReviewAssignment.objects.filter( + Q(is_complete=True) + & Q(reviewer=request.user) + & Q(date_declined__isnull=True), + article__journal=request.journal, + ).count(), + "copyeditor_requests": copyedit_models.CopyeditAssignment.objects.filter( + Q(copyeditor=request.user) + & Q(decision__isnull=True) + & Q(copyedit_reopened__isnull=True), + article__journal=request.journal, + ).count(), + "copyeditor_accepted_requests": copyedit_models.CopyeditAssignment.objects.filter( + Q( + copyeditor=request.user, + decision="accept", + copyeditor_completed__isnull=True, + article__journal=request.journal, + ) + | Q( + copyeditor=request.user, + decision="accept", + copyeditor_completed__isnull=False, + article__journal=request.journal, + copyedit_reopened__isnull=False, + copyedit_reopened_complete__isnull=True, + ) + ).count(), + "copyeditor_completed_requests": copyedit_models.CopyeditAssignment.objects.filter( + (Q(copyeditor=request.user) & Q(copyeditor_completed__isnull=False)) + | ( + Q(copyeditor=request.user) + & Q(copyeditor_completed__isnull=False) + & Q(copyedit_reopened_complete__isnull=False) + ), + article__journal=request.journal, + ).count(), + "typeset_tasks": production_models.TypesetTask.active_objects.filter( assignment__article__journal=request.journal, accepted__isnull=True, completed__isnull=True, - typesetter=request.user).count(), - 'typeset_in_progress_tasks': production_models.TypesetTask.active_objects.filter( + typesetter=request.user, + ).count(), + "typeset_in_progress_tasks": production_models.TypesetTask.active_objects.filter( assignment__article__journal=request.journal, accepted__isnull=False, completed__isnull=True, - typesetter=request.user).count(), - 'typeset_completed_tasks': production_models.TypesetTask.active_objects.filter( + typesetter=request.user, + ).count(), + "typeset_completed_tasks": production_models.TypesetTask.active_objects.filter( assignment__article__journal=request.journal, accepted__isnull=False, completed__isnull=False, - typesetter=request.user).count(), - 'active_submissions': submission_models.Article.objects.filter( - authors=request.user, - journal=request.journal - ).exclude( - stage__in=[submission_models.STAGE_UNSUBMITTED, submission_models.STAGE_PUBLISHED], - ).order_by('-date_submitted'), - 'published_submissions': submission_models.Article.objects.filter( + typesetter=request.user, + ).count(), + "active_submissions": submission_models.Article.objects.filter( + authors=request.user, journal=request.journal + ) + .exclude( + stage__in=[ + submission_models.STAGE_UNSUBMITTED, + submission_models.STAGE_PUBLISHED, + ], + ) + .order_by("-date_submitted"), + "published_submissions": submission_models.Article.objects.filter( authors=request.user, journal=request.journal, stage=submission_models.STAGE_PUBLISHED, - ).order_by('-date_published'), - 'progress_submissions': submission_models.Article.objects.filter( + ).order_by("-date_published"), + "progress_submissions": submission_models.Article.objects.filter( journal=request.journal, owner=request.user, - stage=submission_models.STAGE_UNSUBMITTED).order_by('-date_started'), - 'workflow_elements': workflow.element_names(request.journal.workflow().elements.all()), - 'workflow_element_url': request.GET.get('workflow_element_url', False) + stage=submission_models.STAGE_UNSUBMITTED, + ).order_by("-date_started"), + "workflow_elements": workflow.element_names( + request.journal.workflow().elements.all() + ), + "workflow_element_url": request.GET.get("workflow_element_url", False), } return render(request, template, context) @@ -712,25 +792,27 @@ def dashboard(request): @has_journal @any_editor_user_required def active_submissions(request): - template = 'core/active_submissions.html' + template = "core/active_submissions.html" - active_submissions = submission_models.Article.active_objects.exclude( - stage=submission_models.STAGE_PUBLISHED, - ).filter( - journal=request.journal - ).order_by('pk', 'title') + active_submissions = ( + submission_models.Article.active_objects.exclude( + stage=submission_models.STAGE_PUBLISHED, + ) + .filter(journal=request.journal) + .order_by("pk", "title") + ) if not request.user.is_editor(request) and request.user.is_section_editor(request): active_submissions = logic.filter_articles_to_editor_assigned( - request, - active_submissions + request, active_submissions ) context = { - 'active_submissions': active_submissions, - 'sections': submission_models.Section.objects.filter(is_filterable=True, - journal=request.journal), - 'workflow_element_url': request.GET.get('workflow_element_url', False) + "active_submissions": active_submissions, + "sections": submission_models.Section.objects.filter( + is_filterable=True, journal=request.journal + ), + "workflow_element_url": request.GET.get("workflow_element_url", False), } return render(request, template, context) @@ -740,15 +822,17 @@ def active_submissions(request): @any_editor_user_required def active_submission_filter(request): articles = logic.build_submission_list(request) - html = '' + html = "" for article in articles: - html = html + logic.create_html_snippet('article', article, 'elements/core/submission_list_element.html') + html = html + logic.create_html_snippet( + "article", article, "elements/core/submission_list_element.html" + ) if not articles: - html = '

There are no articles to display

' + html = "

There are no articles to display

" - return HttpResponse(json.dumps({'status': 200, 'html': html})) + return HttpResponse(json.dumps({"status": 200, "html": html})) @has_journal @@ -766,9 +850,9 @@ def dashboard_article(request, article_id): journal=request.journal, ) - template = 'core/article.html' + template = "core/article.html" context = { - 'article': article, + "article": article, } return render(request, template, context) @@ -783,24 +867,25 @@ def manager_index(request): """ if not request.journal: from press import views as press_views + return press_views.manager_index(request) - template = 'core/manager/index.html' + template = "core/manager/index.html" support_message = logic.render_nested_setting( - 'support_contact_message_for_staff', - 'general', + "support_contact_message_for_staff", + "general", request, - nested_settings=[('support_email', 'general')], + nested_settings=[("support_email", "general")], ) context = { - 'published_articles': submission_models.Article.objects.filter( + "published_articles": submission_models.Article.objects.filter( date_published__isnull=False, stage=submission_models.STAGE_PUBLISHED, - journal=request.journal - ).select_related('section')[:25], - 'support_message': support_message, + journal=request.journal, + ).select_related("section")[:25], + "support_message": support_message, } return render(request, template, context) @@ -813,9 +898,9 @@ def flush_cache(request): :return: HttpRedirect """ cache.clear() - messages.add_message(request, messages.SUCCESS, 'Cache has been flushed.') + messages.add_message(request, messages.SUCCESS, "Cache has been flushed.") - return redirect(reverse('core_manager_index')) + return redirect(reverse("core_manager_index")) @editor_user_required @@ -825,15 +910,15 @@ def settings_index(request): :param request: HttpRequest object :return: HttpResponse object """ - settings = models.Setting.objects.all().order_by('name') + settings = models.Setting.objects.all().order_by("name") if not request.user.is_staff: settings = settings.filter( editable_by__in=request.user.roles_for_journal(request.journal) ) - template = 'core/manager/settings/index.html' + template = "core/manager/settings/index.html" context = { - 'settings': settings, + "settings": settings, } return render(request, template, context) @@ -841,7 +926,7 @@ def settings_index(request): @staff_member_required def default_settings_index(request): - """ Proxy view for edit_setting allowing to edit defaults + """Proxy view for edit_setting allowing to edit defaults :param request: HttpRequest object :return: HttpResponse object @@ -865,23 +950,20 @@ def edit_setting(request, setting_group, setting_name): """ with translation.override(request.override_language): setting = models.Setting.objects.get( - name=setting_name, group__name=setting_group) + name=setting_name, group__name=setting_group + ) setting_value = setting_handler.get_setting( - setting_group, - setting_name, - request.journal, - default=False - ) + setting_group, setting_name, request.journal, default=False + ) edit_form = forms.EditKey( - key_type=setting.types, - value=setting_value.value if setting_value else None + key_type=setting.types, value=setting_value.value if setting_value else None ) - if request.POST and 'delete' in request.POST and setting_value: + if request.POST and "delete" in request.POST and setting_value: setting_value.delete() - return redirect(reverse('core_settings_index')) + return redirect(reverse("core_settings_index")) if request.POST: edit_form = forms.EditKey( @@ -891,9 +973,7 @@ def edit_setting(request, setting_group, setting_name): if edit_form.is_valid(): if request.FILES: value = logic.handle_file( - request, - setting_value, - request.FILES['value'] + request, setting_value, request.FILES["value"] ) # for JSON setting we should validate the JSON by attempting @@ -904,7 +984,7 @@ def edit_setting(request, setting_group, setting_name): setting_group, setting_name, request.journal, - edit_form.cleaned_data.get('value'), + edit_form.cleaned_data.get("value"), ) except ValidationError as error: messages.add_message(request, messages.ERROR, error) @@ -913,27 +993,24 @@ def edit_setting(request, setting_group, setting_name): return language_override_redirect( request, - 'core_edit_setting', - { - 'setting_group': setting_group, - 'setting_name': setting_name - }, + "core_edit_setting", + {"setting_group": setting_group, "setting_name": setting_name}, ) - template = 'core/manager/settings/edit_setting.html' + template = "core/manager/settings/edit_setting.html" context = { - 'setting': setting, - 'setting_value': setting_value, - 'group': setting.group, - 'edit_form': edit_form, - 'value': setting_value.value if setting_value else None + "setting": setting, + "setting_value": setting_value, + "group": setting.group, + "edit_form": edit_form, + "value": setting_value.value if setting_value else None, } return render(request, template, context) @staff_member_required def edit_default_setting(request, setting_group, setting_name): - """ Proxy view for edit_setting allowing editing the default value + """Proxy view for edit_setting allowing editing the default value :param request: HttpRequest object :param setting_group: string, SettingGroup.name @@ -960,20 +1037,25 @@ def edit_settings_group(request, display_group): request.user, ) edit_form = forms.GeneratedSettingForm(settings=settings) - attr_form_object, attr_form, display_tabs, fire_redirect = None, None, True, True + attr_form_object, attr_form, display_tabs, fire_redirect = ( + None, + None, + True, + True, + ) - if display_group == 'journal': + if display_group == "journal": attr_form_object = forms.JournalAttributeForm - elif display_group == 'images': + elif display_group == "images": attr_form_object = forms.JournalImageForm display_tabs = False - elif display_group == 'article': + elif display_group == "article": attr_form_object = forms.JournalArticleForm display_tabs = False - elif display_group == 'styling': + elif display_group == "styling": attr_form_object = forms.JournalStylingForm display_tabs = False - elif display_group == 'submission': + elif display_group == "submission": attr_form_object = forms.JournalSubmissionForm if attr_form_object: @@ -1005,8 +1087,10 @@ def edit_settings_group(request, display_group): if attr_form.is_valid(): attr_form.save() - if display_group == 'images': - logic.handle_default_thumbnail(request, request.journal, attr_form) + if display_group == "images": + logic.handle_default_thumbnail( + request, request.journal, attr_form + ) else: fire_redirect = False @@ -1015,24 +1099,26 @@ def edit_settings_group(request, display_group): if fire_redirect: return language_override_redirect( request, - 'core_edit_settings_group', - {'display_group': display_group}, + "core_edit_settings_group", + {"display_group": display_group}, ) - template = 'admin/core/manager/settings/group.html' + template = "admin/core/manager/settings/group.html" context = { - 'group': display_group, - 'settings_list': settings, - 'edit_form': edit_form, - 'attr_form': attr_form, - 'display_tabs': display_tabs, + "group": display_group, + "settings_list": settings, + "edit_form": edit_form, + "attr_form": attr_form, + "display_tabs": display_tabs, } return render(request, template, context) @editor_user_required -def edit_plugin_settings_groups(request, plugin, setting_group_name, journal=None, title=None): +def edit_plugin_settings_groups( + request, plugin, setting_group_name, journal=None, title=None +): """ Allows for editing a group of plugin settings :param request: HttpRequest object @@ -1042,18 +1128,19 @@ def edit_plugin_settings_groups(request, plugin, setting_group_name, journal=Non :param title: an optional argument, page title, string :return: HttpResponse object """ - if journal != '0': + if journal != "0": journal = journal_models.Journal.objects.get(fk=int(journal)) else: journal = None from utils import models as utils_models + plugin = utils_models.Plugin.objects.get(name=plugin) module_name = "{0}.{1}.plugin_settings".format("plugins", plugin.name) plugin_settings = import_module(module_name) - manager_url = getattr(plugin_settings, 'MANAGER_URL', '') - settings = getattr(plugin_settings, setting_group_name, '')() + manager_url = getattr(plugin_settings, "MANAGER_URL", "") + settings = getattr(plugin_settings, setting_group_name, "")() if not settings: raise Http404 @@ -1067,18 +1154,18 @@ def edit_plugin_settings_groups(request, plugin, setting_group_name, journal=Non edit_form.save(plugin=plugin, journal=journal) cache.clear() - return redirect(reverse(request.GET['return'])) + return redirect(reverse(request.GET["return"])) if not title: title = plugin.best_name() - template = 'core/manager/settings/plugin.html' + template = "core/manager/settings/plugin.html" context = { - 'plugin': plugin, - 'settings': settings, - 'edit_form': edit_form, - 'title': title, - 'manager_url': manager_url, + "plugin": plugin, + "settings": settings, + "edit_form": edit_form, + "title": title, + "manager_url": manager_url, } return render(request, template, context) @@ -1091,14 +1178,14 @@ def roles(request): :param request: HttpRequest object :return: HttpResponse object """ - template = 'core/manager/roles/roles.html' + template = "core/manager/roles/roles.html" - roles = models.Role.objects.all().exclude(slug='reader') + roles = models.Role.objects.all().exclude(slug="reader") for role in roles: role.user_count = request.journal.users_with_role_count(role.slug) context = { - 'roles': roles, + "roles": roles, } return render(request, template, context) @@ -1118,41 +1205,41 @@ def role(request, slug): journal=request.journal, role=role_obj, ).select_related( - 'user', - 'journal', + "user", + "journal", ) # Grab additional context for the reviewer page. - if slug == 'reviewer': + if slug == "reviewer": account_roles = account_roles.prefetch_related( - 'user__interest', + "user__interest", ).annotate( total_assignments=Count( - 'user__reviewer', + "user__reviewer", filter=Q(user__reviewer__article__journal=request.journal), ), last_completed_review=Subquery( review_models.ReviewAssignment.objects.filter( - reviewer=OuterRef('user__pk'), + reviewer=OuterRef("user__pk"), is_complete=True, date_complete__isnull=False, article__journal=request.journal, - ).order_by('-date_complete').values('date_complete')[:1] + ) + .order_by("-date_complete") + .values("date_complete")[:1] ), average_score=Subquery( review_models.ReviewerRating.objects.filter( - assignment__reviewer=OuterRef('user__pk') - ).values('assignment__reviewer').annotate( - avg_score=Avg('rating') - ).values('avg_score')[:1] - ) + assignment__reviewer=OuterRef("user__pk") + ) + .values("assignment__reviewer") + .annotate(avg_score=Avg("rating")) + .values("avg_score")[:1] + ), ) - template = 'core/manager/roles/role.html' - context = { - 'role': role_obj, - 'account_roles': account_roles - } + template = "core/manager/roles/role.html" + context = {"role": role_obj, "account_roles": account_roles} return render(request, template, context) @@ -1170,14 +1257,14 @@ def role_action(request, slug, user_id, action): user = get_object_or_404(models.Account, pk=user_id) role_obj = get_object_or_404(models.Role, slug=slug) - if action == 'add': + if action == "add": user.add_account_role(role_slug=slug, journal=request.journal) - elif action == 'remove': + elif action == "remove": user.remove_account_role(role_slug=slug, journal=request.journal) user.save() - return redirect(reverse('core_manager_role', kwargs={'slug': role_obj.slug})) + return redirect(reverse("core_manager_role", kwargs={"slug": role_obj.slug})) @editor_user_required @@ -1188,15 +1275,15 @@ def users(request): :return: HttpResponse object """ if request.POST: - users = request.POST.getlist('users') - role = request.POST.get('role') + users = request.POST.getlist("users") + role = request.POST.get("role") logic.handle_add_users_to_role(users, role, request) - return redirect(reverse('core_manager_users')) + return redirect(reverse("core_manager_users")) - template = 'core/manager/users/index.html' + template = "core/manager/users/index.html" context = { - 'users': request.journal.journal_users(objects=True), - 'roles': models.Role.objects.all().order_by(('name')), + "users": request.journal.journal_users(objects=True), + "roles": models.Role.objects.all().order_by(("name")), } return render(request, template, context) @@ -1209,55 +1296,45 @@ def add_user(request): :return: HttpResponse object """ form = forms.EditAccountForm() - registration_form = forms.AdminUserForm(active='add', request=request) - return_url = request.GET.get('return', None) - role = request.GET.get('role', None) + registration_form = forms.AdminUserForm(active="add", request=request) + return_url = request.GET.get("return", None) + role = request.GET.get("role", None) if request.POST: registration_form = forms.AdminUserForm( - request.POST, - active='add', - request=request + request.POST, active="add", request=request ) if registration_form.is_valid(): new_user = registration_form.save() # Every new user is given the author role if request.journal: - new_user.add_account_role('author', request.journal) + new_user.add_account_role("author", request.journal) if role and request.journal: new_user.add_account_role(role, request.journal) - form = forms.EditAccountForm( - request.POST, - request.FILES, - instance=new_user - ) + form = forms.EditAccountForm(request.POST, request.FILES, instance=new_user) if form.is_valid(): form.save() - messages.add_message( - request, - messages.SUCCESS, - 'User created.' - ) + messages.add_message(request, messages.SUCCESS, "User created.") if return_url: return redirect(return_url) - return redirect(reverse('core_manager_users')) + return redirect(reverse("core_manager_users")) else: # If the registration form is not valid, # we need to add post data to the Edit form for display. form = forms.EditAccountForm(request.POST) - template = 'core/manager/users/edit.html' + template = "core/manager/users/edit.html" context = { - 'form': form, - 'registration_form': registration_form, - 'active': 'add', + "form": form, + "registration_form": registration_form, + "active": "add", } return render(request, template, context) @@ -1276,26 +1353,26 @@ def user_edit(request, user_id): if request.POST: form = forms.EditAccountForm(request.POST, request.FILES, instance=user) - registration_form = forms.AdminUserForm(request.POST, instance=user, request=request) + registration_form = forms.AdminUserForm( + request.POST, instance=user, request=request + ) if form.is_valid() and registration_form.is_valid(): registration_form.save() form.save() - messages.add_message(request, messages.SUCCESS, 'Profile updated.') + messages.add_message(request, messages.SUCCESS, "Profile updated.") - if request.GET.get('return'): - return redirect( - request.GET.get('return') - ) + if request.GET.get("return"): + return redirect(request.GET.get("return")) - return redirect(reverse('core_manager_users')) + return redirect(reverse("core_manager_users")) - template = 'core/manager/users/edit.html' + template = "core/manager/users/edit.html" context = { - 'user_to_edit': user, - 'form': form, - 'registration_form': registration_form, - 'active': 'update', + "user_to_edit": user, + "form": form, + "registration_form": registration_form, + "active": "update", } return render(request, template, context) @@ -1309,9 +1386,9 @@ def inactive_users(request): """ user_list = models.Account.objects.filter(is_active=False) - template = 'core/manager/users/inactive.html' + template = "core/manager/users/inactive.html" context = { - 'users': user_list, + "users": user_list, } return render(request, template, context) @@ -1329,13 +1406,13 @@ def logged_in_users(request): for session in sessions: data = session.get_decoded() - user_id_list.append(data.get('_auth_user_id', None)) + user_id_list.append(data.get("_auth_user_id", None)) users = models.Account.objects.filter(id__in=user_id_list) - template = 'core/manager/users/logged_in_users.html' + template = "core/manager/users/logged_in_users.html" context = { - 'users': users, + "users": users, } return render(request, template, context) @@ -1358,19 +1435,18 @@ def user_history(request, user_id): is_email=True, ) - template = 'core/manager/users/history.html' + template = "core/manager/users/history.html" context = { - 'user': user, - 'review_assignments': review_models.ReviewAssignment.objects.filter( + "user": user, + "review_assignments": review_models.ReviewAssignment.objects.filter( reviewer=user, article__journal=request.journal, ), - 'copyedit_assignments': - copyedit_models.CopyeditAssignment.objects.filter( - copyeditor=user, - article__journal=request.journal, - ), - 'log_entries': log_entries, + "copyedit_assignments": copyedit_models.CopyeditAssignment.objects.filter( + copyeditor=user, + article__journal=request.journal, + ), + "log_entries": log_entries, } return render(request, template, context) @@ -1379,39 +1455,39 @@ def user_history(request, user_id): @editor_user_required def enrol_users(request): user_search = [] - first_name = request.GET.get('first_name', '') - last_name = request.GET.get('last_name', '') - email = request.GET.get('email', '') + first_name = request.GET.get("first_name", "") + last_name = request.GET.get("last_name", "") + email = request.GET.get("email", "") assignable_roles = models.Role.objects.exclude( - slug__in=['reader'], - ).order_by(('name')) + slug__in=["reader"], + ).order_by(("name")) # if the current user is not staff, exclude the journal-manager role. if not request.user.is_staff: assignable_roles = assignable_roles.exclude( - slug='journal-manager', + slug="journal-manager", ) if first_name or last_name or email: filters = {} if first_name and len(first_name) >= 2: - filters['first_name__icontains'] = first_name + filters["first_name__icontains"] = first_name if last_name and len(last_name) >= 2: - filters['last_name__icontains'] = last_name + filters["last_name__icontains"] = last_name if email and len(email) >= 2: - filters['email__icontains'] = email + filters["email__icontains"] = email user_search = core_models.Account.objects.filter(**filters) - template = 'core/manager/users/enrol_users.html' + template = "core/manager/users/enrol_users.html" context = { - 'user_search': user_search, - 'roles': assignable_roles, - 'first_name': first_name, - 'last_name': last_name, - 'email': email, - 'return': request.GET.get('return'), - 'reader': models.Role.objects.get(slug='reader'), + "user_search": user_search, + "roles": assignable_roles, + "first_name": first_name, + "last_name": last_name, + "email": email, + "return": request.GET.get("return"), + "reader": models.Role.objects.get(slug="reader"), } return render(request, template, context) @@ -1422,45 +1498,67 @@ def settings_home(request): # 1. An excluded list of homepage items # 2. A list of active homepage items - active_elements = models.HomepageElement.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk, active=True) + active_elements = models.HomepageElement.objects.filter( + content_type=request.model_content_type, + object_id=request.site_type.pk, + active=True, + ) active_pks = [f.pk for f in active_elements.all()] if request.press and not request.journal: - elements = models.HomepageElement.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk, - available_to_press=True).exclude(pk__in=active_pks) + elements = models.HomepageElement.objects.filter( + content_type=request.model_content_type, + object_id=request.site_type.pk, + available_to_press=True, + ).exclude(pk__in=active_pks) else: - elements = models.HomepageElement.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk).exclude(pk__in=active_pks) - - if 'add' in request.POST: - element_id = request.POST.get('add') - homepage_element = get_object_or_404(models.HomepageElement, pk=element_id, - content_type=request.model_content_type, object_id=request.site_type.pk) - if homepage_element.name == 'Carousel' and request.journal and not request.journal.default_large_image: - messages.add_message(request, messages.WARNING, 'You cannot enable the carousel until you add a default' - 'large image file.') + elements = models.HomepageElement.objects.filter( + content_type=request.model_content_type, object_id=request.site_type.pk + ).exclude(pk__in=active_pks) + + if "add" in request.POST: + element_id = request.POST.get("add") + homepage_element = get_object_or_404( + models.HomepageElement, + pk=element_id, + content_type=request.model_content_type, + object_id=request.site_type.pk, + ) + if ( + homepage_element.name == "Carousel" + and request.journal + and not request.journal.default_large_image + ): + messages.add_message( + request, + messages.WARNING, + "You cannot enable the carousel until you add a default" + "large image file.", + ) else: homepage_element.active = True homepage_element.save() - return redirect(reverse('home_settings_index')) + return redirect(reverse("home_settings_index")) - if 'delete' in request.POST: - element_id = request.POST.get('delete') - homepage_element = get_object_or_404(models.HomepageElement, pk=element_id, - content_type=request.model_content_type, object_id=request.site_type.pk) + if "delete" in request.POST: + element_id = request.POST.get("delete") + homepage_element = get_object_or_404( + models.HomepageElement, + pk=element_id, + content_type=request.model_content_type, + object_id=request.site_type.pk, + ) homepage_element.active = False homepage_element.save() - return redirect(reverse('home_settings_index')) + return redirect(reverse("home_settings_index")) - template = 'core/manager/settings/index_home.html' + template = "core/manager/settings/index_home.html" context = { - 'active_elements': active_elements, - 'elements': elements, + "active_elements": active_elements, + "elements": elements, } return render(request, template, context) @@ -1474,15 +1572,18 @@ def journal_home_order(request): :return: HttpResponse """ if request.POST: - ids = request.POST.getlist('element[]') + ids = request.POST.getlist("element[]") ids = [int(_id) for _id in ids] - for he in models.HomepageElement.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk, active=True): + for he in models.HomepageElement.objects.filter( + content_type=request.model_content_type, + object_id=request.site_type.pk, + active=True, + ): he.sequence = ids.index(he.pk) he.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @editor_user_required @@ -1494,9 +1595,9 @@ def article_images(request): """ articles = submission_models.Article.objects.filter(journal=request.journal) - template = 'core/manager/images/articles.html' + template = "core/manager/images/articles.html" context = { - 'articles': articles, + "articles": articles, } return render(request, template, context) @@ -1511,41 +1612,55 @@ def article_image_edit(request, article_pk): :param article_pk: Article object PK :return: HttpResponse object """ - article = get_object_or_404(submission_models.Article, pk=article_pk, journal=request.journal) + article = get_object_or_404( + submission_models.Article, pk=article_pk, journal=request.journal + ) article_meta_image_form = forms.ArticleMetaImageForm(instance=article) - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - file_to_delete = get_object_or_404(models.File, pk=delete_id, article_id=article_pk) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + file_to_delete = get_object_or_404( + models.File, pk=delete_id, article_id=article_pk + ) article_files = [article.thumbnail_image_file, article.large_image_file] - if file_to_delete in article_files and request.user.is_staff or request.user == file_to_delete.owner: + if ( + file_to_delete in article_files + and request.user.is_staff + or request.user == file_to_delete.owner + ): file_to_delete.delete() - return redirect(reverse('core_article_image_edit', kwargs={'article_pk': article.pk})) + return redirect( + reverse("core_article_image_edit", kwargs={"article_pk": article.pk}) + ) - if request.POST and request.FILES and 'large' in request.POST: - uploaded_file = request.FILES.get('image_file') + if request.POST and request.FILES and "large" in request.POST: + uploaded_file = request.FILES.get("image_file") logic.handle_article_large_image_file(uploaded_file, article, request) - elif request.POST and request.FILES and 'thumb' in request.POST: - uploaded_file = request.FILES.get('image_file') + elif request.POST and request.FILES and "thumb" in request.POST: + uploaded_file = request.FILES.get("image_file") logic.handle_article_thumb_image_file(uploaded_file, article, request) - elif request.POST and request.FILES and 'meta' in request.POST: + elif request.POST and request.FILES and "meta" in request.POST: article.unlink_meta_file() - article_meta_image_form = forms.ArticleMetaImageForm(request.POST, request.FILES, instance=article) + article_meta_image_form = forms.ArticleMetaImageForm( + request.POST, request.FILES, instance=article + ) if article_meta_image_form.is_valid(): article_meta_image_form.save() if request.POST: flush_cache(request) - return redirect(reverse('core_article_image_edit', kwargs={'article_pk': article.pk})) + return redirect( + reverse("core_article_image_edit", kwargs={"article_pk": article.pk}) + ) - template = 'core/manager/images/article_image.html' + template = "core/manager/images/article_image.html" context = { - 'article': article, - 'article_meta_image_form': article_meta_image_form, + "article": article, + "article_meta_image_form": article_meta_image_form, } return render(request, template, context) @@ -1564,8 +1679,8 @@ def contacts(request): object_id=request.site_type.pk, ) - if 'delete' in request.POST: - contact_id = request.POST.get('delete') + if "delete" in request.POST: + contact_id = request.POST.get("delete") contact = get_object_or_404( models.Contacts, pk=contact_id, @@ -1573,7 +1688,7 @@ def contacts(request): object_id=request.site_type.pk, ) contact.delete() - return redirect(reverse('core_journal_contacts')) + return redirect(reverse("core_journal_contacts")) if request.POST: form = forms.JournalContactForm(request.POST) @@ -1584,13 +1699,13 @@ def contacts(request): contact.object_id = request.site_type.pk contact.sequence = request.site_type.next_contact_order() contact.save() - return redirect(reverse('core_journal_contacts')) + return redirect(reverse("core_journal_contacts")) - template = 'core/manager/contacts/index.html' + template = "core/manager/contacts/index.html" context = { - 'form': form, - 'contacts': contacts, - 'action': 'new', + "form": form, + "contacts": contacts, + "action": "new", } return render(request, template, context) @@ -1634,14 +1749,14 @@ def edit_contacts(request, contact_id=None): return language_override_redirect( request, - 'core_journal_contact', - {'contact_id': contact.pk}, + "core_journal_contact", + {"contact_id": contact.pk}, ) - template = 'core/manager/contacts/manage.html' + template = "core/manager/contacts/manage.html" context = { - 'form': form, - 'contact': contact, + "form": form, + "contact": contact, } return render(request, template, context) @@ -1655,14 +1770,16 @@ def contacts_order(request): :return: HttpResponse object """ if request.POST: - ids = request.POST.getlist('contact[]') + ids = request.POST.getlist("contact[]") ids = [int(_id) for _id in ids] - for jc in models.Contacts.objects.filter(content_type=request.model_content_type, object_id=request.site_type.pk): + for jc in models.Contacts.objects.filter( + content_type=request.model_content_type, object_id=request.site_type.pk + ): jc.sequence = ids.index(jc.pk) jc.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @editor_user_required @@ -1678,7 +1795,7 @@ def editorial_team(request): journal=request.journal, ) settings, setting_group = logic.get_settings_to_edit( - 'editorial', + "editorial", request.journal, request.user, ) @@ -1698,20 +1815,22 @@ def editorial_team(request): clear_cache() return language_override_redirect( request, - 'core_editorial_team', + "core_editorial_team", kwargs={}, ) - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - group = get_object_or_404(models.EditorialGroup, pk=delete_id, journal=request.journal) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + group = get_object_or_404( + models.EditorialGroup, pk=delete_id, journal=request.journal + ) group.delete() - return redirect(reverse('core_editorial_team')) + return redirect(reverse("core_editorial_team")) - template = 'core/manager/editorial/index.html' + template = "core/manager/editorial/index.html" context = { - 'editorial_groups': editorial_groups, - 'edit_form': edit_form, + "editorial_groups": editorial_groups, + "edit_form": edit_form, } return render(request, template, context) @@ -1743,9 +1862,7 @@ def edit_editorial_group(request, group_id=None): next_sequence = request.journal.next_group_order() except AttributeError: next_sequence = request.press.next_group_order() - form = forms.EditorialGroupForm( - next_sequence=next_sequence - ) + form = forms.EditorialGroupForm(next_sequence=next_sequence) if request.POST: form = forms.EditorialGroupForm(request.POST, instance=group) @@ -1760,14 +1877,14 @@ def edit_editorial_group(request, group_id=None): return language_override_redirect( request, - 'core_edit_editorial_team', - {'group_id': group.pk}, + "core_edit_editorial_team", + {"group_id": group.pk}, ) - template = 'core/manager/editorial/manage_group.html' + template = "core/manager/editorial/manage_group.html" context = { - 'group': group, - 'form': form, + "group": group, + "form": form, } return render(request, template, context) @@ -1785,9 +1902,7 @@ def add_member_to_group(request, group_id, user_id=None): :return: """ group = get_object_or_404( - models.EditorialGroup, - pk=group_id, - journal=request.journal + models.EditorialGroup, pk=group_id, journal=request.journal ) journal_users = request.journal.journal_users(objects=True) members = [member.user for member in group.editorialgroupmember_set.all()] @@ -1795,39 +1910,28 @@ def add_member_to_group(request, group_id, user_id=None): # Drop users thagit t are in both lists. user_list = list(set(journal_users) ^ set(members)) - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - membership = get_object_or_404( - models.EditorialGroupMember, - pk=delete_id - ) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + membership = get_object_or_404(models.EditorialGroupMember, pk=delete_id) membership.delete() return redirect( - reverse( - 'core_editorial_member_to_group', - kwargs={'group_id': group.pk} - ) + reverse("core_editorial_member_to_group", kwargs={"group_id": group.pk}) ) if user_id: user_to_add = get_object_or_404(models.Account, pk=user_id) if user_to_add not in members: models.EditorialGroupMember.objects.create( - group=group, - user=user_to_add, - sequence=group.next_member_sequence() + group=group, user=user_to_add, sequence=group.next_member_sequence() ) return redirect( - reverse( - 'core_editorial_member_to_group', - kwargs={'group_id': group.pk} - ) + reverse("core_editorial_member_to_group", kwargs={"group_id": group.pk}) ) - template = 'core/manager/editorial/add_member.html' + template = "core/manager/editorial/add_member.html" context = { - 'group': group, - 'users': user_list, + "group": group, + "users": user_list, } return render(request, template, context) @@ -1860,25 +1964,25 @@ def plugin_list(request): try: module_name = "{0}.{1}.plugin_settings".format("plugins", plugin.name) plugin_settings = import_module(module_name) - manager_url = getattr(plugin_settings, 'MANAGER_URL', '') + manager_url = getattr(plugin_settings, "MANAGER_URL", "") if manager_url: reverse(manager_url) plugin_list.append( - {'model': plugin, - 'manager_url': manager_url, - 'name': getattr(plugin_settings, 'PLUGIN_NAME') - }, + { + "model": plugin, + "manager_url": manager_url, + "name": getattr(plugin_settings, "PLUGIN_NAME"), + }, ) except (ImportError, NoReverseMatch) as e: failed_to_load.append(plugin) logger.error("Importing plugin %s failed: %s" % (plugin, e)) logger.exception(e) - - template = 'core/manager/plugins.html' + template = "core/manager/plugins.html" context = { - 'plugins': plugin_list, - 'failed_to_load': failed_to_load, + "plugins": plugin_list, + "failed_to_load": failed_to_load, } return render(request, template, context) @@ -1893,15 +1997,17 @@ def editorial_ordering(request, type_to_order, group_id=None): :param group_id: EditorialGroup PK, optional :return: HttpRespons eobject """ - if type_to_order == 'group': - ids = request.POST.getlist('group[]') + if type_to_order == "group": + ids = request.POST.getlist("group[]") objects = models.EditorialGroup.objects.filter(journal=request.journal) - elif type_to_order == 'sections': - ids = request.POST.getlist('section[]') + elif type_to_order == "sections": + ids = request.POST.getlist("section[]") objects = submission_models.Section.objects.filter(journal=request.journal) else: - group = get_object_or_404(models.EditorialGroup, pk=group_id, journal=request.journal) - ids = request.POST.getlist('member[]') + group = get_object_or_404( + models.EditorialGroup, pk=group_id, journal=request.journal + ) + ids = request.POST.getlist("member[]") objects = models.EditorialGroupMember.objects.filter(group=group) ids = [int(_id) for _id in ids] @@ -1910,7 +2016,7 @@ def editorial_ordering(request, type_to_order, group_id=None): _object.sequence = ids.index(_object.pk) _object.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @has_journal @@ -1921,35 +2027,43 @@ def kanban(request): :param request: HttpRequest object :return: HttpResponse object """ - unassigned_articles = submission_models.Article.objects.filter(Q(stage=submission_models.STAGE_UNASSIGNED), - journal=request.journal) \ - .order_by('-date_submitted') + unassigned_articles = submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_UNASSIGNED), journal=request.journal + ).order_by("-date_submitted") - in_review = submission_models.Article.objects.filter(Q(stage=submission_models.STAGE_ASSIGNED) | - Q(stage=submission_models.STAGE_UNDER_REVIEW) | - Q(stage=submission_models.STAGE_UNDER_REVISION), - journal=request.journal) \ - .order_by('-date_submitted') + in_review = submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_ASSIGNED) + | Q(stage=submission_models.STAGE_UNDER_REVIEW) + | Q(stage=submission_models.STAGE_UNDER_REVISION), + journal=request.journal, + ).order_by("-date_submitted") - copyediting = submission_models.Article.objects.filter(Q(stage=submission_models.STAGE_ACCEPTED) | - Q(stage__in=submission_models.COPYEDITING_STAGES), - journal=request.journal) \ - .order_by('-date_submitted') + copyediting = submission_models.Article.objects.filter( + Q(stage=submission_models.STAGE_ACCEPTED) + | Q(stage__in=submission_models.COPYEDITING_STAGES), + journal=request.journal, + ).order_by("-date_submitted") - assigned_table = production_models.ProductionAssignment.objects.filter(article__journal=request.journal) + assigned_table = production_models.ProductionAssignment.objects.filter( + article__journal=request.journal + ) assigned = [assignment.article.pk for assignment in assigned_table] prod_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_TYPESETTING, journal=request.journal) + stage=submission_models.STAGE_TYPESETTING, journal=request.journal + ) assigned_articles = submission_models.Article.objects.filter(pk__in=assigned) proofing_assigned_table = proofing_models.ProofingAssignment.objects.filter( article__journal=request.journal, ) - proofing_assigned = [assignment.article.pk for assignment in proofing_assigned_table] + proofing_assigned = [ + assignment.article.pk for assignment in proofing_assigned_table + ] proof_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_PROOFING, journal=request.journal) + stage=submission_models.STAGE_PROOFING, journal=request.journal + ) proof_assigned_articles = submission_models.Article.objects.filter( pk__in=proofing_assigned, ) @@ -1957,24 +2071,24 @@ def kanban(request): prepub = submission_models.Article.objects.filter( Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION), journal=request.journal, - ).order_by('-date_submitted') + ).order_by("-date_submitted") articles_in_workflow_plugins = workflow.articles_in_workflow_plugins(request) context = { - 'unassigned_articles': unassigned_articles, - 'in_review': in_review, - 'copyediting': copyediting, - 'production': prod_articles, - 'production_assigned': assigned_articles, - 'proofing': proof_articles, - 'proofing_assigned': proof_assigned_articles, - 'prepubs': prepub, - 'articles_in_workflow_plugins': articles_in_workflow_plugins, - 'workflow': request.journal.workflow() + "unassigned_articles": unassigned_articles, + "in_review": in_review, + "copyediting": copyediting, + "production": prod_articles, + "production_assigned": assigned_articles, + "proofing": proof_articles, + "proofing_assigned": proof_assigned_articles, + "prepubs": prepub, + "articles_in_workflow_plugins": articles_in_workflow_plugins, + "workflow": request.journal.workflow(), } - template = 'core/kanban.html' + template = "core/kanban.html" return render(request, template, context) @@ -1991,7 +2105,7 @@ def delete_note(request, article_id, note_id): note = get_object_or_404(submission_models.Note, pk=note_id) note.delete() - url = reverse('kanban_home') + url = reverse("kanban_home") return redirect("{0}?article_id={1}".format(url, article_id)) @@ -2003,15 +2117,19 @@ def manage_notifications(request, notification_id=None): form = forms.NotificationForm() if notification_id: - notification = get_object_or_404(journal_models.Notifications, pk=notification_id) + notification = get_object_or_404( + journal_models.Notifications, pk=notification_id + ) form = forms.NotificationForm(instance=notification) if request.POST: - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - notification_to_delete = get_object_or_404(journal_models.Notifications, pk=delete_id) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + notification_to_delete = get_object_or_404( + journal_models.Notifications, pk=delete_id + ) notification_to_delete.delete() - return redirect(reverse('core_manager_notifications')) + return redirect(reverse("core_manager_notifications")) if notification: form = forms.NotificationForm(request.POST, instance=notification) @@ -2023,13 +2141,13 @@ def manage_notifications(request, notification_id=None): save_notification.journal = request.journal save_notification.save() - return redirect(reverse('core_manager_notifications')) + return redirect(reverse("core_manager_notifications")) - template = 'core/manager/notifications/manage_notifications.html' + template = "core/manager/notifications/manage_notifications.html" context = { - 'notifications': notifications, - 'notification': notification, - 'form': form, + "notifications": notifications, + "notification": notification, + "form": form, } return render(request, template, context) @@ -2043,19 +2161,19 @@ def email_templates(request): :return: HttpResponse object """ template_list = ( - (setting, 'subject_%s' % setting.name) - for setting in models.Setting.objects.filter(group__name='email') + (setting, "subject_%s" % setting.name) + for setting in models.Setting.objects.filter(group__name="email") ) - template = 'core/manager/email/email_templates.html' + template = "core/manager/email/email_templates.html" context = { - 'template_list': template_list, + "template_list": template_list, } return render(request, template, context) -@role_can_access('sections') +@role_can_access("sections") def section_list(request): """ Displays a list of the journals sections. @@ -2066,8 +2184,8 @@ def section_list(request): journal=request.journal, ) - if request.POST and 'delete' in request.POST: - section_id = request.POST.get('delete') + if request.POST and "delete" in request.POST: + section_id = request.POST.get("delete") section_to_delete = get_object_or_404(submission_models.Section, pk=section_id) if section_to_delete.article_count(): @@ -2075,22 +2193,22 @@ def section_list(request): request, messages.WARNING, _( - 'You cannot remove a section that contains articles. Remove articles' - ' from the section if you want to delete it.' + "You cannot remove a section that contains articles. Remove articles" + " from the section if you want to delete it." ), ) else: section_to_delete.delete() - return redirect(reverse('core_manager_sections')) + return redirect(reverse("core_manager_sections")) - template = 'core/manager/sections/section_list.html' + template = "core/manager/sections/section_list.html" context = { - 'section_objects': section_objects, + "section_objects": section_objects, } return render(request, template, context) -@role_can_access('sections') +@role_can_access("sections") @GET_language_override def manage_section(request, section_id=None): """ @@ -2100,8 +2218,13 @@ def manage_section(request, section_id=None): :return: HttpResponse object """ with translation.override(request.override_language): - section = get_object_or_404(submission_models.Section, pk=section_id, - journal=request.journal) if section_id else None + section = ( + get_object_or_404( + submission_models.Section, pk=section_id, journal=request.journal + ) + if section_id + else None + ) sections = submission_models.Section.objects.filter(journal=request.journal) if section: @@ -2110,9 +2233,10 @@ def manage_section(request, section_id=None): form = forms.SectionForm(request=request) if request.POST: - if section: - form = forms.SectionForm(request.POST, instance=section, request=request) + form = forms.SectionForm( + request.POST, instance=section, request=request + ) else: form = forms.SectionForm(request.POST, request=request) @@ -2124,20 +2248,20 @@ def manage_section(request, section_id=None): return language_override_redirect( request, - 'core_manager_section', - {'section_id': section.pk if section else form_section.pk}, + "core_manager_section", + {"section_id": section.pk if section else form_section.pk}, ) - template = 'core/manager/sections/manage_section.html' + template = "core/manager/sections/manage_section.html" context = { - 'sections': sections, - 'section': section, - 'form': form, + "sections": sections, + "section": section, + "form": form, } return render(request, template, context) -@role_can_access('sections') +@role_can_access("sections") def section_articles(request, section_id): """ Displays a list of articles in a given section. @@ -2147,9 +2271,9 @@ def section_articles(request, section_id): pk=section_id, journal=request.journal, ) - template = 'core/manager/sections/section_articles.html' + template = "core/manager/sections/section_articles.html" context = { - 'section': section, + "section": section, } return render(request, template, context) @@ -2160,34 +2284,41 @@ def pinned_articles(request): Allows an Editor to pin articles to the top of the article page. :param request: HttpRequest object """ - pinned_articles = journal_models.PinnedArticle.objects.filter(journal=request.journal) + pinned_articles = journal_models.PinnedArticle.objects.filter( + journal=request.journal + ) published_articles = logic.get_unpinned_articles(request, pinned_articles) if request.POST: - if 'pin' in request.POST: - article_id = request.POST.get('pin') - article = get_object_or_404(submission_models.Article, pk=article_id, journal=request.journal) + if "pin" in request.POST: + article_id = request.POST.get("pin") + article = get_object_or_404( + submission_models.Article, pk=article_id, journal=request.journal + ) journal_models.PinnedArticle.objects.create( article=article, journal=request.journal, - sequence=request.journal.next_pa_seq()) - messages.add_message(request, messages.INFO, 'Article pinned.') + sequence=request.journal.next_pa_seq(), + ) + messages.add_message(request, messages.INFO, "Article pinned.") - if 'unpin' in request.POST: - article_id = request.POST.get('unpin') - pinned_article = get_object_or_404(journal_models.PinnedArticle, journal=request.journal, pk=article_id) + if "unpin" in request.POST: + article_id = request.POST.get("unpin") + pinned_article = get_object_or_404( + journal_models.PinnedArticle, journal=request.journal, pk=article_id + ) pinned_article.delete() - messages.add_message(request, messages.INFO, 'Article unpinned.') + messages.add_message(request, messages.INFO, "Article unpinned.") - if 'orders[]' in request.POST: + if "orders[]" in request.POST: logic.order_pinned_articles(request, pinned_articles) - return redirect(reverse('core_pinned_articles')) + return redirect(reverse("core_pinned_articles")) - template = 'core/manager/pinned_articles.html' + template = "core/manager/pinned_articles.html" context = { - 'pinned_articles': pinned_articles, - 'published_articles': published_articles, + "pinned_articles": pinned_articles, + "published_articles": published_articles, } return render(request, template, context) @@ -2211,42 +2342,28 @@ def journal_workflow(request): available_elements = logic.get_available_elements(journal_workflow) if request.POST: - if 'element_name' in request.POST: - element_name = request.POST.get('element_name') + if "element_name" in request.POST: + element_name = request.POST.get("element_name") element = logic.handle_element_post(journal_workflow, element_name, request) if element: journal_workflow.elements.add(element) - messages.add_message( - request, - messages.SUCCESS, - 'Element added.' - ) + messages.add_message(request, messages.SUCCESS, "Element added.") else: - messages.add_message( - request, - messages.WARNING, - 'Element not found.' - ) + messages.add_message(request, messages.WARNING, "Element not found.") - if 'delete' in request.POST: - delete_id = request.POST.get('delete') + if "delete" in request.POST: + delete_id = request.POST.get("delete") delete_element = get_object_or_404( - models.WorkflowElement, - journal=request.journal, - pk=delete_id - ) - workflow.remove_element( - request, - journal_workflow, - delete_element + models.WorkflowElement, journal=request.journal, pk=delete_id ) + workflow.remove_element(request, journal_workflow, delete_element) - return redirect(reverse('core_journal_workflow')) + return redirect(reverse("core_journal_workflow")) - template = 'core/workflow.html' + template = "core/workflow.html" context = { - 'workflow': journal_workflow, - 'available_elements': available_elements, + "workflow": journal_workflow, + "available_elements": available_elements, } return render(request, template, context) @@ -2262,14 +2379,14 @@ def order_workflow_elements(request): workflow = models.Workflow.objects.get(journal=request.journal) if request.POST: - ids = [int(_id) for _id in request.POST.getlist('element[]')] + ids = [int(_id) for _id in request.POST.getlist("element[]")] for element in workflow.elements.all(): order = ids.index(element.pk) element.order = order element.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @ensure_csrf_cookie @@ -2280,17 +2397,17 @@ def set_session_timezone(request): if chosen_timezone in pytz.all_timezones_set: request.session["janeway_timezone"] = chosen_timezone status = 200 - response_data['message'] = 'OK' + response_data["message"] = "OK" logger.debug("Timezone set to %s for this session" % chosen_timezone) else: status = 404 - response_data['message'] = 'Timezone not found: %s' % chosen_timezone + response_data["message"] = "Timezone not found: %s" % chosen_timezone response_data = {} return HttpResponse( - content=json.dumps(response_data), - content_type='application/json', - status=200, + content=json.dumps(response_data), + content_type="application/json", + status=200, ) @@ -2300,12 +2417,14 @@ def request_submission_access(request): check = rm.RepositoryRole.objects.filter( repository=request.repository, user=request.user, - role__slug='author', + role__slug="author", ).exists() elif request.journal: check = request.user.is_author(request) else: - raise Http404('The Submission Access page is only accessible on Repository and Journal sites.') + raise Http404( + "The Submission Access page is only accessible on Repository and Journal sites." + ) active_request = models.AccessRequest.objects.filter( user=request.user, @@ -2313,7 +2432,7 @@ def request_submission_access(request): repository=request.repository, processed=False, ).first() - role = models.Role.objects.get(slug='author') + role = models.Role.objects.get(slug="author") form = forms.AccessRequestForm( journal=request.journal, repository=request.repository, @@ -2332,29 +2451,25 @@ def request_submission_access(request): if form.is_valid(): access_request = form.save() event_kwargs = { - 'request': request, - 'access_request': access_request, + "request": request, + "access_request": access_request, } events_logic.Events.raise_event( - 'on_access_request', + "on_access_request", **event_kwargs, ) messages.add_message( request, messages.SUCCESS, - 'Access Request Sent.', - ) - return redirect( - reverse( - 'request_submission_access' - ) + "Access Request Sent.", ) + return redirect(reverse("request_submission_access")) - template = 'admin/core/request_submission_access.html' + template = "admin/core/request_submission_access.html" context = { - 'check': check, - 'active_request': active_request, - 'form': form, + "check": check, + "active_request": active_request, + "form": form, } return render( request, @@ -2367,7 +2482,9 @@ def request_submission_access(request): def manage_access_requests(request): # If we don't have a journal or repository return a 404. if not request.journal and not request.repository: - raise Http404('The Submission Access page is only accessible on Repository and Journal sites.') + raise Http404( + "The Submission Access page is only accessible on Repository and Journal sites." + ) active_requests = models.AccessRequest.objects.filter( journal=request.journal, @@ -2375,10 +2492,10 @@ def manage_access_requests(request): processed=False, ) if request.POST: - if 'approve' in request.POST: - pk = request.POST.get('approve') + if "approve" in request.POST: + pk = request.POST.get("approve") access_request = active_requests.get(pk=pk) - decision = 'approve' + decision = "approve" if request.journal: access_request.user.add_account_role( @@ -2389,40 +2506,40 @@ def manage_access_requests(request): rm.RepositoryRole.objects.get_or_create( repository=access_request.repository, user=access_request.user, - role=access_request.role + role=access_request.role, ) - elif 'reject' in request.POST: - pk = request.POST.get('reject') + elif "reject" in request.POST: + pk = request.POST.get("reject") access_request = active_requests.get(pk=pk) - decision = 'reject' + decision = "reject" if access_request: - eval_note = request.POST.get('eval_note') + eval_note = request.POST.get("eval_note") access_request.evaluation_note = eval_note access_request.processed = True access_request.save() event_kwargs = { - 'decision': decision, - 'access_request': access_request, - 'request': request, + "decision": decision, + "access_request": access_request, + "request": request, } events_logic.Events.raise_event( - 'on_access_request_complete', + "on_access_request_complete", **event_kwargs, ) messages.add_message( request, messages.SUCCESS, - 'Access Request Processed.', + "Access Request Processed.", ) return redirect( reverse( - 'manage_access_requests', + "manage_access_requests", ) ) - template = 'admin/core/manage_access_requests.html' + template = "admin/core/manage_access_requests.html" context = { - 'active_requests': active_requests, + "active_requests": active_requests, } return render( request, @@ -2441,7 +2558,7 @@ def sitemap(request, path_parts): try: return files.serve_sitemap_file(path_parts) except FileNotFoundError: - logger.warning('Sitemap for {} not found.'.format(request.journal.name)) + logger.warning("Sitemap for {} not found.".format(request.journal.name)) raise Http404() @@ -2451,21 +2568,22 @@ class GenericFacetedListView(generic.ListView): This is a generic base class for creating filterable list views with Janeway models. """ + model = NotImplementedField template_name = NotImplementedField - paginate_by = '25' + paginate_by = "25" facets = {} # These fields will receive a single initial value, not a list - single_value_fields = {'date_time', 'date', 'integer', 'search', 'boolean'} + single_value_fields = {"date_time", "date", "integer", "search", "boolean"} # None or integer action_queryset_chunk_size = None def get_paginate_by(self, queryset): - paginate_by = self.request.GET.get('paginate_by', self.paginate_by) - if paginate_by == 'all': + paginate_by = self.request.GET.get("paginate_by", self.paginate_by) + if paginate_by == "all": if queryset: paginate_by = len(queryset) else: @@ -2476,34 +2594,34 @@ def get_context_data(self, **kwargs): params_querydict = self.request.GET.copy() context = super().get_context_data(**kwargs) queryset = self.get_queryset() - context['paginate_by'] = params_querydict.get('paginate_by', self.paginate_by) + context["paginate_by"] = params_querydict.get("paginate_by", self.paginate_by) facets = self.get_facets() initial = dict(params_querydict.lists()) for keyword, value in initial.items(): if keyword in facets: - if facets[keyword]['type'] in self.single_value_fields: + if facets[keyword]["type"] in self.single_value_fields: initial[keyword] = value[0] - context['facet_form'] = forms.CBVFacetForm( + context["facet_form"] = forms.CBVFacetForm( queryset=queryset, facets=facets, initial=initial, ) - context['actions'] = self.get_actions() + context["actions"] = self.get_actions() - params_querydict.pop('action_status', '') - params_querydict.pop('action_error', '') - context['params_string'] = params_querydict.urlencode() - context['action_maximum_size'] = setting_handler.get_setting( - 'Identifiers', - 'doi_manager_action_maximum_size', + params_querydict.pop("action_status", "") + params_querydict.pop("action_error", "") + context["params_string"] = params_querydict.urlencode() + context["action_maximum_size"] = setting_handler.get_setting( + "Identifiers", + "doi_manager_action_maximum_size", self.request.journal if self.request.journal else None, ).processed_value - context['order_by'] = params_querydict.get('order_by', self.get_order_by()) - context['order_by_choices'] = self.get_order_by_choices() + context["order_by"] = params_querydict.get("order_by", self.get_order_by()) + context["order_by_choices"] = self.get_order_by_choices() return context def get_queryset(self, params_querydict=None): @@ -2511,22 +2629,22 @@ def get_queryset(self, params_querydict=None): params_querydict = self.request.GET.copy() # Clear any previous action status and error - params_querydict.pop('action_status', '') - params_querydict.pop('action_error', False) + params_querydict.pop("action_status", "") + params_querydict.pop("action_error", False) # Clear order_by, since it is handled separately - params_querydict.pop('order_by', '') + params_querydict.pop("order_by", "") self.queryset = super().get_queryset() q_stack = [] facets = self.get_facets() for facet in facets.values(): - self.queryset = self.queryset.annotate(**facet.get('annotations', {})) + self.queryset = self.queryset.annotate(**facet.get("annotations", {})) for keyword, value_list in params_querydict.lists(): # The following line prevents the user from passing any parameters # other than those specified in the facets. if keyword in facets and value_list: - if facets[keyword]['type'] == 'search' and value_list[0]: + if facets[keyword]["type"] == "search" and value_list[0]: self.queryset, _duplicates = search_model_admin( self.request, self.model, @@ -2534,9 +2652,9 @@ def get_queryset(self, params_querydict=None): queryset=self.queryset, ) predicates = [] - elif facets[keyword]['type'] == 'boolean' and value_list[0] != '': + elif facets[keyword]["type"] == "boolean" and value_list[0] != "": # All = None, Yes = 1, No = 0 - predicates = [(f'{keyword}__exact', value_list[0])] + predicates = [(f"{keyword}__exact", value_list[0])] elif value_list[0]: predicates = [(keyword, value) for value in value_list] else: @@ -2557,7 +2675,7 @@ def order_queryset(self, queryset): return queryset def get_order_by(self): - chosen_order_by = self.request.GET.get('order_by', '') + chosen_order_by = self.request.GET.get("order_by", "") order_by_choices = self.get_order_by_choices() if chosen_order_by in dict(order_by_choices): return chosen_order_by @@ -2565,17 +2683,17 @@ def get_order_by(self): try: return order_by_choices[0][0] except IndexError: - return '' + return "" def get_order_by_choices(self): - """ Subclass must implement to allow ordering result set + """Subclass must implement to allow ordering result set :return: A list of 2-item tuples compatible with Django choices eg: [("choice_a", "Choice A"), ("choice_b", "Choice B")] """ return [] def get_facets(self): - """ Subclass must implement to declare available facets""" + """Subclass must implement to declare available facets""" facets = {} return self.filter_facets_if_journal(facets) @@ -2591,15 +2709,14 @@ def get_actions(self): return [] def post(self, request, *args, **kwargs): - - params_string = request.POST.get('params_string') + params_string = request.POST.get("params_string") params_querydict = QueryDict(params_string, mutable=True) actions = self.get_actions() if actions: start = time.time() - action_status = '' + action_status = "" action_error = False querysets = [] @@ -2607,17 +2724,21 @@ def post(self, request, *args, **kwargs): if request.journal: querysets.extend(self.split_up_queryset_if_needed(queryset)) - elif hasattr(self.model, 'journal'): + elif hasattr(self.model, "journal"): for journal in journal_models.Journal.objects.all(): journal_queryset = queryset.filter(journal=journal) if journal_queryset: - querysets.extend(self.split_up_queryset_if_needed(journal_queryset)) + querysets.extend( + self.split_up_queryset_if_needed(journal_queryset) + ) for action in actions: - kwargs = {'start': start} - if action.get('name') in request.POST: + kwargs = {"start": start} + if action.get("name") in request.POST: for queryset in querysets: - action_status, action_error = action.get('action')(queryset, **kwargs) + action_status, action_error = action.get("action")( + queryset, **kwargs + ) messages.add_message( request, messages.INFO if not action_error else messages.ERROR, @@ -2625,27 +2746,27 @@ def post(self, request, *args, **kwargs): ) if params_string: - return redirect(f'{request.path}?{params_string}') + return redirect(f"{request.path}?{params_string}") else: return redirect(request.path) def split_up_queryset_if_needed(self, queryset): if self.action_queryset_chunk_size: n = self.action_queryset_chunk_size - querysets = [queryset[i:i + n] for i in range(0, queryset.count(), n)] + querysets = [queryset[i : i + n] for i in range(0, queryset.count(), n)] return querysets else: return [queryset] def get_journal_filter_query(self): - if self.request.journal and hasattr(self.model, 'journal'): + if self.request.journal and hasattr(self.model, "journal"): return Q(journal=self.request.journal) else: return Q() def filter_facets_if_journal(self, facets): if self.request.journal: - facets.pop('journal__pk', '') + facets.pop("journal__pk", "") return facets else: return facets @@ -2657,60 +2778,59 @@ class FilteredArticlesListView(GenericFacetedListView): """ model = submission_models.Article - template_name = 'core/manager/article_list.html' + template_name = "core/manager/article_list.html" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) raise DeprecationWarning( - 'This view is deprecated. Use GenericFacetedListView instead.' + "This view is deprecated. Use GenericFacetedListView instead." ) -@method_decorator(editor_user_required, name='dispatch') +@method_decorator(editor_user_required, name="dispatch") class BaseUserList(GenericFacetedListView): - model = core_models.Account - template_name = 'core/manager/users/list.html' + template_name = "core/manager/users/list.html" def get_facets(self): facets = { - 'q': { - 'type': 'search', - 'field_label': 'Search', + "q": { + "type": "search", + "field_label": "Search", }, - 'is_active': { - 'type': 'boolean', - 'field_label': 'Active status', - 'true_label': 'Active', - 'false_label': 'Inactive', + "is_active": { + "type": "boolean", + "field_label": "Active status", + "true_label": "Active", + "false_label": "Inactive", }, - 'is_staff': { - 'type': 'boolean', - 'field_label': 'Staff member', - 'true_label': 'Staff', - 'false_label': 'Not staff', + "is_staff": { + "type": "boolean", + "field_label": "Staff member", + "true_label": "Staff", + "false_label": "Not staff", }, - 'accountrole__role__pk': { - 'type': 'foreign_key', - 'model': models.Role, - 'field_label': 'Role', - 'choice_label_field': 'name', + "accountrole__role__pk": { + "type": "foreign_key", + "model": models.Role, + "field_label": "Role", + "choice_label_field": "name", }, - 'accountrole__journal__pk': { - 'type': 'foreign_key', - 'model': journal_models.Journal, - 'field_label': 'Journal', - 'choice_label_field': 'name', + "accountrole__journal__pk": { + "type": "foreign_key", + "model": journal_models.Journal, + "field_label": "Journal", + "choice_label_field": "name", }, } return self.filter_facets_if_journal(facets) def get_order_by_choices(self): return [ - ('-date_joined', _('Newest')), - ('date_joined', _('Oldest')), - ('last_name', _('Last name A-Z')), - ('-last_name', _('Last name Z-A')), + ("-date_joined", _("Newest")), + ("date_joined", _("Oldest")), + ("last_name", _("Last name A-Z")), + ("-last_name", _("Last name Z-A")), ] def get_journal_filter_query(self): @@ -2721,8 +2841,8 @@ def get_journal_filter_query(self): def filter_facets_if_journal(self, facets): if self.request.journal: - facets.pop('accountrole__journal__pk', '') - facets.pop('is_staff', '') + facets.pop("accountrole__journal__pk", "") + facets.pop("is_staff", "") return facets else: return facets @@ -2730,47 +2850,52 @@ def filter_facets_if_journal(self, facets): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - roles = models.Role.objects.exclude(slug='reader') + roles = models.Role.objects.exclude(slug="reader") if not self.request.user.is_staff: - roles = roles.exclude(slug='journal-manager') - context['roles'] = roles + roles = roles.exclude(slug="journal-manager") + context["roles"] = roles - accountrole_form = forms.AccountRoleForm({ - 'journal': self.request.journal, - }) + accountrole_form = forms.AccountRoleForm( + { + "journal": self.request.journal, + } + ) if self.request.journal: - accountrole_form.fields['journal'].widget.choices = [ + accountrole_form.fields["journal"].widget.choices = [ (self.request.journal.pk, self.request.journal.name) ] else: journal_names = core_models.SettingValue.objects.filter( - setting__group__name='general', - setting__name='journal_name', + setting__group__name="general", + setting__name="journal_name", journal__isnull=False, - ).order_by('value') - choices = [(None, '---------')] + ).order_by("value") + choices = [(None, "---------")] choices.extend([(n.journal.pk, n.value) for n in journal_names]) - accountrole_form.fields['journal'].widget.choices = choices - context['accountrole_form'] = accountrole_form + accountrole_form.fields["journal"].widget.choices = choices + context["accountrole_form"] = accountrole_form return context def post(self, request, *args, **kwargs): - - if 'remove_accountrole' in request.POST: + if "remove_accountrole" in request.POST: accountrole = core_models.AccountRole.objects.get( - pk=request.POST.get('remove_accountrole') + pk=request.POST.get("remove_accountrole") + ) + message = ( + f"{accountrole.role} role removed " + f"from {accountrole.user} in {accountrole.journal.name}." ) - message = f'{accountrole.role} role removed ' \ - f'from {accountrole.user} in {accountrole.journal.name}.' accountrole.delete() messages.success(request, message) - elif 'role' in request.POST: + elif "role" in request.POST: form = forms.AccountRoleForm(request.POST) if form.is_valid(): accountrole = form.save() - message = f'{accountrole.role} role added ' \ - f'for {accountrole.user} in {accountrole.journal.name}.' + message = ( + f"{accountrole.role} role added " + f"for {accountrole.user} in {accountrole.journal.name}." + ) messages.success(request, message) return super().post(request, *args, **kwargs) diff --git a/src/core/widgets.py b/src/core/widgets.py index 205d8c4e44..7d133b7a87 100644 --- a/src/core/widgets.py +++ b/src/core/widgets.py @@ -2,7 +2,8 @@ class JanewayFileInput(ClearableFileInput): - template_name = 'admin/core/widgets/janeway_clearable_file.html' + template_name = "admin/core/widgets/janeway_clearable_file.html" + class TableMultiSelectUser(CheckboxSelectMultiple): - template_name = 'admin/core/widgets/multi_checkbox_user_table.html' + template_name = "admin/core/widgets/multi_checkbox_user_table.html" diff --git a/src/core/workflow.py b/src/core/workflow.py index 513b1c9bcd..b2336e3ce7 100755 --- a/src/core/workflow.py +++ b/src/core/workflow.py @@ -18,26 +18,24 @@ ELEMENT_STAGES = { - 'review': submission_models.REVIEW_STAGES, - 'copyediting': submission_models.COPYEDITING_STAGES, - 'production': [submission_models.STAGE_TYPESETTING], - 'proofing': [submission_models.STAGE_PROOFING], - 'prepublication': [submission_models.STAGE_READY_FOR_PUBLICATION] + "review": submission_models.REVIEW_STAGES, + "copyediting": submission_models.COPYEDITING_STAGES, + "production": [submission_models.STAGE_TYPESETTING], + "proofing": [submission_models.STAGE_PROOFING], + "prepublication": [submission_models.STAGE_READY_FOR_PUBLICATION], } STAGES_ELEMENTS = { - submission_models.STAGE_ASSIGNED: 'review', - submission_models.STAGE_UNDER_REVIEW: 'review', - submission_models.STAGE_UNDER_REVISION: 'review', - submission_models.STAGE_ACCEPTED: 'review', - - submission_models.STAGE_EDITOR_COPYEDITING: 'copyediting', - submission_models.STAGE_AUTHOR_COPYEDITING: 'copyediting', - submission_models.STAGE_FINAL_COPYEDITING: 'copyediting', - - submission_models.STAGE_TYPESETTING: 'production', - submission_models.STAGE_PROOFING: 'proofing', - submission_models.STAGE_READY_FOR_PUBLICATION: 'prepublication', + submission_models.STAGE_ASSIGNED: "review", + submission_models.STAGE_UNDER_REVIEW: "review", + submission_models.STAGE_UNDER_REVISION: "review", + submission_models.STAGE_ACCEPTED: "review", + submission_models.STAGE_EDITOR_COPYEDITING: "copyediting", + submission_models.STAGE_AUTHOR_COPYEDITING: "copyediting", + submission_models.STAGE_FINAL_COPYEDITING: "copyediting", + submission_models.STAGE_TYPESETTING: "production", + submission_models.STAGE_PROOFING: "proofing", + submission_models.STAGE_READY_FOR_PUBLICATION: "prepublication", } @@ -48,10 +46,10 @@ def workflow_element_complete(**kwargs): :return: HttpRedirect """ - handshake_url = kwargs.get('handshake_url') - request = kwargs.get('request') - article = kwargs.get('article') - switch_stage = kwargs.get('switch_stage') + handshake_url = kwargs.get("handshake_url") + request = kwargs.get("request") + article = kwargs.get("article") + switch_stage = kwargs.get("switch_stage") if not handshake_url or not request or not article: raise Http404 @@ -72,10 +70,10 @@ def workflow_next(handshake_url, request, article, switch_stage=False): workflow = models.Workflow.objects.get(journal=request.journal) workflow_elements = workflow.elements.all() - if handshake_url == 'submit_review': + if handshake_url == "submit_review": set_stage(article) clear_cache() - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) current_element = workflow.elements.get(handshake_url=handshake_url) @@ -85,7 +83,9 @@ def workflow_next(handshake_url, request, article, switch_stage=False): next_element = workflow_elements[index] except IndexError: # An index error will occur here when the workflow is complete - return redirect(reverse('manage_archive_article', kwargs={'article_id': article.pk})) + return redirect( + reverse("manage_archive_article", kwargs={"article_id": article.pk}) + ) if switch_stage: log_stage_change(article, next_element) @@ -94,33 +94,35 @@ def workflow_next(handshake_url, request, article, switch_stage=False): article.save() if ( - request.user.is_staff or - request.user.is_editor(request=request) or - request.user in article.editor_list() + request.user.is_staff + or request.user.is_editor(request=request) + or request.user in article.editor_list() ): try: - response = redirect(reverse( - next_element.jump_url, - kwargs={'article_id': article.pk}, - )) + response = redirect( + reverse( + next_element.jump_url, + kwargs={"article_id": article.pk}, + ) + ) except NoReverseMatch: try: response = redirect(reverse(next_element.handshake_url)) except NoReverseMatch: - response = redirect(reverse('core_dashboard')) + response = redirect(reverse("core_dashboard")) except Exception as e: logger.exception(e) # Fallback here. if not response: - response = redirect(reverse('core_dashboard')) + response = redirect(reverse("core_dashboard")) messages.add_message( request, messages.SUCCESS, - '%s stage completed for article: %d' - '' % (capfirst(current_element.element_name), article.pk), + "%s stage completed for article: %d" + "" % (capfirst(current_element.element_name), article.pk), ) return response @@ -159,13 +161,15 @@ def create_default_workflow(journal): workflow, c = models.Workflow.objects.get_or_create(journal=journal) for index, element in enumerate(models.BASE_ELEMENTS): - e, c = models.WorkflowElement.objects.get_or_create(journal=journal, - element_name=element.get('name'), - handshake_url=element['handshake_url'], - stage=element['stage'], - jump_url=element['jump_url'], - article_url=element['article_url'], - defaults={'order': index}) + e, c = models.WorkflowElement.objects.get_or_create( + journal=journal, + element_name=element.get("name"), + handshake_url=element["handshake_url"], + stage=element["stage"], + jump_url=element["jump_url"], + article_url=element["article_url"], + defaults={"order": index}, + ) workflow.elements.add(e) @@ -185,15 +189,17 @@ def articles_in_workflow_plugins(request): for element in workflow.elements.all(): if element.element_name in settings.WORKFLOW_PLUGINS: try: - settings_module = import_module(settings.WORKFLOW_PLUGINS[element.element_name]) + settings_module = import_module( + settings.WORKFLOW_PLUGINS[element.element_name] + ) element_dict = { - 'articles': submission_models.Article.objects.filter( + "articles": submission_models.Article.objects.filter( stage=element.stage, journal=request.journal, ), - 'name': element.element_name, - 'template': settings_module.KANBAN_CARD, + "name": element.element_name, + "template": settings_module.KANBAN_CARD, } workflow_list[element.element_name] = element_dict @@ -204,7 +210,7 @@ def articles_in_workflow_plugins(request): def core_workflow_element_names(): - return [element.get('name') for element in models.BASE_ELEMENTS] + return [element.get("name") for element in models.BASE_ELEMENTS] def element_names(elements): @@ -231,15 +237,13 @@ def remove_element(request, journal_workflow, element): messages.add_message( request, messages.WARNING, - 'Element cannot be removed as there are {0}' - ' articles in this stage.'.format(articles.count()) + "Element cannot be removed as there are {0}" + " articles in this stage.".format(articles.count()), ) else: journal_workflow.elements.remove(element) messages.add_message( - request, - messages.SUCCESS, - 'Element removed from workflow.' + request, messages.SUCCESS, "Element removed from workflow." ) @@ -255,12 +259,10 @@ def workflow_plugin_settings(element): ) return { - 'display_name': getattr(settings_module, 'DISPLAY_NAME', ''), - 'description': getattr(settings_module, 'DESCRIPTION', ''), - 'kanban_card': getattr(settings_module, 'KANBAN_CARD', ''), - 'dashboard_template': getattr( - settings_module, 'DASHBOARD_TEMPLATE', '' - ) + "display_name": getattr(settings_module, "DISPLAY_NAME", ""), + "description": getattr(settings_module, "DESCRIPTION", ""), + "kanban_card": getattr(settings_module, "KANBAN_CARD", ""), + "dashboard_template": getattr(settings_module, "DASHBOARD_TEMPLATE", ""), } except (ImportError, KeyError) as e: @@ -274,9 +276,9 @@ def workflow_auto_assign_editors(**kwargs): Handler for auto assignment of editors :param kwargs: A dict containing three keys handshake_url, request, article and optionally switch_stage """ - article = kwargs.get('article') - request = kwargs.get('request') - skip = kwargs.get('skip', False) + article = kwargs.get("article") + request = kwargs.get("request") + skip = kwargs.get("skip", False) if article and article.section and article.section.auto_assign_editors: section = article.section @@ -310,7 +312,5 @@ def workflow_journal_choices(journal): for element in workflow.elements.all(): for element_stage in ELEMENT_STAGES[element.element_name]: - choices.append( - [element.stage, element_stage] - ) + choices.append([element.stage, element_stage]) return choices diff --git a/src/cron/admin.py b/src/cron/admin.py index 02d95dda2e..92a97d7e08 100755 --- a/src/cron/admin.py +++ b/src/cron/admin.py @@ -11,42 +11,57 @@ class CronTaskAdmin(admin.ModelAdmin): - list_display = ('task_type', 'run_at', 'article') - list_filter = ('article__journal__code', 'email_journal', - 'task_type', 'added', 'run_at') - search_fields = ('article__title', 'email_to', 'email_cc', - 'email_html', 'email_bcc', 'task_data') - raw_id_fields = ('article',) + list_display = ("task_type", "run_at", "article") + list_filter = ( + "article__journal__code", + "email_journal", + "task_type", + "added", + "run_at", + ) + search_fields = ( + "article__title", + "email_to", + "email_cc", + "email_html", + "email_bcc", + "task_data", + ) + raw_id_fields = ("article",) class SentReminderAdmin(admin.ModelAdmin): - list_display = ('type', '_object', 'sent') - list_filter = (admin_utils.SentReminderJournalFilter, - 'type', 'sent') - search_fields = ('type', 'object_id') - date_hierarchy = ('sent') - readonly_fields = ('_object',) + list_display = ("type", "_object", "sent") + list_filter = (admin_utils.SentReminderJournalFilter, "type", "sent") + search_fields = ("type", "object_id") + date_hierarchy = "sent" + readonly_fields = ("_object",) def _object(self, obj): if not obj: - return '' + return "" - if obj.type == 'review': + if obj.type == "review": model = review_models.ReviewAssignment - elif obj.type == 'accepted-review': + elif obj.type == "accepted-review": model = review_models.ReviewAssignment - elif obj.type == 'revisions': + elif obj.type == "revisions": model = review_models.RevisionRequest return model.objects.get(id=obj.object_id) class ReminderAdmin(admin.ModelAdmin): - list_display = ('type', 'run_type', - 'template_name', 'days', 'target_date', - 'subject', 'journal') - list_filter = ('journal__code', 'type', 'run_type', 'days', - 'template_name') - search_fields = ('journal__code', 'type', 'run_type', 'template_name') + list_display = ( + "type", + "run_type", + "template_name", + "days", + "target_date", + "subject", + "journal", + ) + list_filter = ("journal__code", "type", "run_type", "days", "template_name") + search_fields = ("journal__code", "type", "run_type", "template_name") admin_list = [ diff --git a/src/cron/forms.py b/src/cron/forms.py index ae71a70491..3e2cf282fd 100755 --- a/src/cron/forms.py +++ b/src/cron/forms.py @@ -11,13 +11,12 @@ class ReminderForm(forms.ModelForm): - class Meta: model = models.Reminder - exclude = ('journal',) + exclude = ("journal",) def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal') + self.journal = kwargs.pop("journal") super(ReminderForm, self).__init__(*args, **kwargs) def save(self, commit=True): diff --git a/src/cron/logic.py b/src/cron/logic.py index 59611dac01..3fd875d7dd 100755 --- a/src/cron/logic.py +++ b/src/cron/logic.py @@ -13,81 +13,101 @@ def task_runner(task): - if task.task_type == 'slack_message': + if task.task_type == "slack_message": pass - elif task.task_type == 'email_message': - log_dict = {'level': 'Info', 'action_text': task.task_data, 'types': task.task_type, - 'target': task.article} - notify.notification(**{'action': ['email'], 'task': task, 'log_dict': log_dict}) + elif task.task_type == "email_message": + log_dict = { + "level": "Info", + "action_text": task.task_data, + "types": task.task_type, + "target": task.article, + } + notify.notification(**{"action": ["email"], "task": task, "log_dict": log_dict}) def process_editor_digest(journal, user_role): - unassigned_articles = submission_models.Article.objects.filter(stage=submission_models.STAGE_UNASSIGNED, - journal=journal) - overdue_reviews = review_models.ReviewAssignment.objects.filter(date_complete__isnull=False, - date_declined__isnull=True, - is_complete=False, - date_due__lte=timezone.now(), - article__journal=journal) - overdue_revisions = review_models.RevisionRequest.objects.filter(date_completed__isnull=True, - date_due__lte=timezone.now(), - article__journal=journal) - overdue_proofing = proofing_models.ProofingTask.objects.filter(completed__isnull=True, - cancelled__isnull=True, - due__lte=timezone.now(), - round__assignment__article__journal=journal) - awaiting_publication = submission_models.Article.objects.filter(stage=submission_models.STAGE_READY_FOR_PUBLICATION, - journal=journal) + unassigned_articles = submission_models.Article.objects.filter( + stage=submission_models.STAGE_UNASSIGNED, journal=journal + ) + overdue_reviews = review_models.ReviewAssignment.objects.filter( + date_complete__isnull=False, + date_declined__isnull=True, + is_complete=False, + date_due__lte=timezone.now(), + article__journal=journal, + ) + overdue_revisions = review_models.RevisionRequest.objects.filter( + date_completed__isnull=True, + date_due__lte=timezone.now(), + article__journal=journal, + ) + overdue_proofing = proofing_models.ProofingTask.objects.filter( + completed__isnull=True, + cancelled__isnull=True, + due__lte=timezone.now(), + round__assignment__article__journal=journal, + ) + awaiting_publication = submission_models.Article.objects.filter( + stage=submission_models.STAGE_READY_FOR_PUBLICATION, journal=journal + ) context = { - 'unassigned_articles': unassigned_articles, - 'overdue_reviews': overdue_reviews, - 'overdue_revisions': overdue_revisions, - 'overdue_proofing': overdue_proofing, - 'awaiting_publication': awaiting_publication, + "unassigned_articles": unassigned_articles, + "overdue_reviews": overdue_reviews, + "overdue_revisions": overdue_revisions, + "overdue_proofing": overdue_proofing, + "awaiting_publication": awaiting_publication, } - return render_template.get_requestless_content(context, journal, 'editor_digest') + return render_template.get_requestless_content(context, journal, "editor_digest") def process_revision_digest(journal, user_role): - pending_requests = review_models.RevisionRequest.objects.filter(article__correspondence_author=user_role.user, - date_completed__isnull=True, - date_due__gte=timezone.now()) - overdue_requests = review_models.RevisionRequest.objects.filter(article__correspondence_author=user_role.user, - date_completed__isnull=True, - date_due__lte=timezone.now()) + pending_requests = review_models.RevisionRequest.objects.filter( + article__correspondence_author=user_role.user, + date_completed__isnull=True, + date_due__gte=timezone.now(), + ) + overdue_requests = review_models.RevisionRequest.objects.filter( + article__correspondence_author=user_role.user, + date_completed__isnull=True, + date_due__lte=timezone.now(), + ) context = { - 'pending_requests': pending_requests, - 'overdue_requests': overdue_requests, + "pending_requests": pending_requests, + "overdue_requests": overdue_requests, } - return render_template.get_requestless_content(context, journal, 'revision_digest') + return render_template.get_requestless_content(context, journal, "revision_digest") def process_reviewer_digest(journal, user_role): - pending_requests = review_models.ReviewAssignment.objects.filter(reviewer=user_role.user, - date_complete__isnull=True, - date_due__gte=timezone.now()) - overdue_requests = review_models.ReviewAssignment.objects.filter(reviewer=user_role.user, - date_complete__isnull=True, - date_due__lte=timezone.now()) + pending_requests = review_models.ReviewAssignment.objects.filter( + reviewer=user_role.user, + date_complete__isnull=True, + date_due__gte=timezone.now(), + ) + overdue_requests = review_models.ReviewAssignment.objects.filter( + reviewer=user_role.user, + date_complete__isnull=True, + date_due__lte=timezone.now(), + ) context = { - 'pending_requests': pending_requests, - 'overdue_requests': overdue_requests, + "pending_requests": pending_requests, + "overdue_requests": overdue_requests, } - return render_template.get_requestless_content(context, journal, 'reviewer_digest') + return render_template.get_requestless_content(context, journal, "reviewer_digest") def process_digest_items(journal, user_role): text = None - if user_role.role.slug == 'editor': + if user_role.role.slug == "editor": text = process_editor_digest(journal, user_role) - elif user_role.role.slug == 'author': + elif user_role.role.slug == "author": text = process_revision_digest(journal, user_role) - elif user_role.role.slug == 'reviewer': + elif user_role.role.slug == "reviewer": text = process_reviewer_digest(journal, user_role) return text @@ -95,7 +115,7 @@ def process_digest_items(journal, user_role): def check_template_exists(request, reminder): try: - request.journal.get_setting('email', reminder.template_name) + request.journal.get_setting("email", reminder.template_name) return True except BaseException: return False diff --git a/src/cron/management/commands/execute_cron_tasks.py b/src/cron/management/commands/execute_cron_tasks.py index 28da9bd46e..0452740b83 100755 --- a/src/cron/management/commands/execute_cron_tasks.py +++ b/src/cron/management/commands/execute_cron_tasks.py @@ -19,8 +19,8 @@ def handle(self, *args, **options): :return: None """ print("Executing cron tasks now.") - call_command('send_digest_emails') - call_command('send_reminders') - call_command('poll_crossref') - call_command('clearsessions') + call_command("send_digest_emails") + call_command("send_reminders") + call_command("poll_crossref") + call_command("clearsessions") models.CronTask.run_tasks() diff --git a/src/cron/management/commands/install_cron.py b/src/cron/management/commands/install_cron.py index 63ca4c1a4c..66af4ebb1f 100755 --- a/src/cron/management/commands/install_cron.py +++ b/src/cron/management/commands/install_cron.py @@ -24,7 +24,7 @@ class Command(BaseCommand): help = "Installs a cron tasks." def add_arguments(self, parser): - parser.add_argument('--action', default="") + parser.add_argument("--action", default="") def handle(self, *args, **options): """Installs Cron @@ -33,7 +33,7 @@ def handle(self, *args, **options): :param options: None :return: None """ - if not os.path.isfile('/usr/bin/crontab'): + if not os.path.isfile("/usr/bin/crontab"): print("WARNING: /usr/bin/crontab not found, skipping crontab config.") return @@ -41,46 +41,46 @@ def handle(self, *args, **options): print("WARNING: crontab module is not installed, skipping crontab config.") return - action = options.get('action') + action = options.get("action") tab = crontab.CronTab(user=True) - virtualenv = os.environ.get('VIRTUAL_ENV', None) + virtualenv = os.environ.get("VIRTUAL_ENV", None) - cwd = settings.PROJECT_DIR.replace('/', '_') + cwd = settings.PROJECT_DIR.replace("/", "_") jobs = [ { - 'name': '{}_janeway_cron_job'.format(cwd), - 'time': 30, - 'task': 'execute_cron_tasks', - 'type': 'mins', + "name": "{}_janeway_cron_job".format(cwd), + "time": 30, + "task": "execute_cron_tasks", + "type": "mins", }, { - 'name': '{}_janeway_ithenticate_job'.format(cwd), - 'time': 30, - 'task': 'store_ithenticate_scores', - 'type': 'mins', + "name": "{}_janeway_ithenticate_job".format(cwd), + "time": 30, + "task": "store_ithenticate_scores", + "type": "mins", }, { - 'name': '{}_janeway_sitemaps_job'.format(cwd), - 'time': 4, - 'task': 'generate_sitemaps', - 'type': 'hourly', + "name": "{}_janeway_sitemaps_job".format(cwd), + "time": 4, + "task": "generate_sitemaps", + "type": "hourly", }, { - 'name': '{}_janeway_reader_notifications'.format(cwd), - 'time': 23, - 'task': 'send_publication_notifications', - 'type': 'daily', + "name": "{}_janeway_reader_notifications".format(cwd), + "time": 23, + "task": "send_publication_notifications", + "type": "daily", }, ] if settings.ENABLE_ENHANCED_MAILGUN_FEATURES: jobs.append( { - 'name': '{}_janeway_mailgun_job'.format(cwd), - 'time': 59, - 'task': 'check_mailgun_stat', - 'type': 'mins', + "name": "{}_janeway_mailgun_job".format(cwd), + "time": 59, + "task": "check_mailgun_stat", + "type": "mins", } ) @@ -88,38 +88,40 @@ def handle(self, *args, **options): task_time, task_type = settings.SITE_SEARCH_INDEXING_FREQUENCY jobs.append( { - 'name': '{}_site_search_data'.format(cwd), - 'time': task_time, - 'task': 'generate_site_search_data', - 'type': task_type, + "name": "{}_site_search_data".format(cwd), + "time": task_time, + "task": "generate_site_search_data", + "type": task_type, } ) for job in jobs: - current_job = find_job(tab, job['name']) + current_job = find_job(tab, job["name"]) if not current_job: - django_command = "{0}/manage.py {1}".format(settings.BASE_DIR, job['task']) + django_command = "{0}/manage.py {1}".format( + settings.BASE_DIR, job["task"] + ) if virtualenv: - command = '%s/bin/python3 %s' % (virtualenv, django_command) + command = "%s/bin/python3 %s" % (virtualenv, django_command) else: - command = '%s' % (django_command) + command = "%s" % (django_command) - cron_job = tab.new(command, comment=job['name']) + cron_job = tab.new(command, comment=job["name"]) - if job.get('type') == 'daily': - cron_job.setall('0 {} * * *'.format(job['time'])) - elif job.get('type') == 'hourly': - cron_job.setall('0 */{} * * *'.format(job['time'])) + if job.get("type") == "daily": + cron_job.setall("0 {} * * *".format(job["time"])) + elif job.get("type") == "hourly": + cron_job.setall("0 */{} * * *".format(job["time"])) else: - cron_job.minute.every(job['time']) + cron_job.minute.every(job["time"]) else: - print("{name} cron job already exists.".format(name=job['name'])) + print("{name} cron job already exists.".format(name=job["name"])) - if action == 'test': + if action == "test": print(tab.render()) - elif action == 'quiet': + elif action == "quiet": pass else: tab.write() diff --git a/src/cron/management/commands/send_digest_emails.py b/src/cron/management/commands/send_digest_emails.py index 230cd92dfc..d22eec9422 100755 --- a/src/cron/management/commands/send_digest_emails.py +++ b/src/cron/management/commands/send_digest_emails.py @@ -13,7 +13,6 @@ class Command(BaseCommand): help = "Sends digest emails" def handle(self, *args, **options): - journals = journal_models.Journal.objects.all() for journal in journals: @@ -23,15 +22,17 @@ def handle(self, *args, **options): for user in users: print("Processing user {0}".format(user.full_name())) - user_roles = models.AccountRole.objects.filter(user=user, journal=journal) + user_roles = models.AccountRole.objects.filter( + user=user, journal=journal + ) - text = '' + text = "" for user_role in user_roles: print("Processing role {0}".format(user_role.role.name)) items = logic.process_digest_items(journal, user_role) if items: - text = text + '\n\n' + items + text = text + "\n\n" + items print(text) print("-------------------------------------------") diff --git a/src/cron/management/commands/send_publication_notifications.py b/src/cron/management/commands/send_publication_notifications.py index 3969b4f976..0a2f043336 100644 --- a/src/cron/management/commands/send_publication_notifications.py +++ b/src/cron/management/commands/send_publication_notifications.py @@ -28,33 +28,31 @@ class Command(BaseCommand): help = "Sends out article/issue publication notifications to users in the reader role.." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) + parser.add_argument("journal_code", nargs="?", default=None) def handle(self, *args, **options): - journal_code = options.get('journal_code', None) + journal_code = options.get("journal_code", None) journals = journal_models.Journal.objects.all() if journal_code: journals = journals.filter(code=journal_code) for journal in journals: if setting_handler.get_setting( - setting_group_name='notifications', - setting_name='send_reader_notifications', + setting_group_name="notifications", + setting_name="send_reader_notifications", journal=journal, ).value: - print('Sending notification for {}'.format(journal.name)) - readers = journal.users_with_role('reader') + print("Sending notification for {}".format(journal.name)) + readers = journal.users_with_role("reader") bcc_list = [reader.email for reader in readers] if bcc_list: - print("Sending notifications to {}".format( - ", ".join(bcc_list) - )) + print("Sending notifications to {}".format(", ".join(bcc_list))) today = timezone.now().today().date() start = timezone.now().replace(hour=0, minute=0, second=0) @@ -67,39 +65,50 @@ def handle(self, *args, **options): ) if articles_published_today.exists(): context = { - 'articles': articles_published_today, - 'journal': journal, + "articles": articles_published_today, + "journal": journal, } html = render_template.get_requestless_content( context=context, journal=journal, - template='reader_publication_notification', + template="reader_publication_notification", ) - html = html + "

You can unsubscribe from publication notifications on your profile page: {}

".format( - journal.site_url( - reverse( - 'core_edit_profile', + html = ( + html + + "

You can unsubscribe from publication notifications on your profile page: {}

".format( + journal.site_url( + reverse( + "core_edit_profile", + ) ) ) ) notify_helpers.send_email_with_body_from_user( request=create_fake_request(journal), - subject=setting_handler.get_setting('email_subject', 'subject_reader_publication_notification', journal).value, - to=setting_handler.get_setting('general', 'from_address', journal).value, + subject=setting_handler.get_setting( + "email_subject", + "subject_reader_publication_notification", + journal, + ).value, + to=setting_handler.get_setting( + "general", "from_address", journal + ).value, body=html, bcc=bcc_list, log_dict={ - 'level': 'Info', - 'action_text': 'Publication notification sent', - 'types': 'Publication Notification', - 'target': journal, - } + "level": "Info", + "action_text": "Publication notification sent", + "types": "Publication Notification", + "target": journal, + }, ) else: print("No articles were published today.") else: - print('Reader publication notifications are not enabled for {}'.format(journal.name)) - - + print( + "Reader publication notifications are not enabled for {}".format( + journal.name + ) + ) diff --git a/src/cron/management/commands/send_reminders.py b/src/cron/management/commands/send_reminders.py index 112d3c45bf..5c874fc248 100755 --- a/src/cron/management/commands/send_reminders.py +++ b/src/cron/management/commands/send_reminders.py @@ -12,20 +12,28 @@ class Command(BaseCommand): help = "Sends review and revision reminder emails.." def add_arguments(self, parser): - parser.add_argument('--action', default="") + parser.add_argument("--action", default="") def handle(self, *args, **options): - action = options.get('action') + action = options.get("action") journals = journal_models.Journal.objects.all() for journal in journals: - print("Processing reminders for journal {0}: {1}".format(journal.pk, journal.name)) + print( + "Processing reminders for journal {0}: {1}".format( + journal.pk, journal.name + ) + ) reminders = models.Reminder.objects.filter(journal=journal) for reminder in reminders: - print("Reminder {0}, target date: {1}".format(reminder, reminder.target_date())) - if action == 'test': + print( + "Reminder {0}, target date: {1}".format( + reminder, reminder.target_date() + ) + ) + if action == "test": reminder.send_reminder(test=True) else: reminder.send_reminder() diff --git a/src/cron/middleware.py b/src/cron/middleware.py index 455a7cf853..229b5f62a0 100755 --- a/src/cron/middleware.py +++ b/src/cron/middleware.py @@ -11,10 +11,9 @@ class CronMiddleware(BaseMiddleware): - @staticmethod def process_request(request): - """ This middleware class calls the Cron runner to process scheduled tasks (like emails) + """This middleware class calls the Cron runner to process scheduled tasks (like emails) :param request: the current request :return: None diff --git a/src/cron/migrations/0001_initial.py b/src/cron/migrations/0001_initial.py index 87d9dc82fa..f2db7f6779 100755 --- a/src/cron/migrations/0001_initial.py +++ b/src/cron/migrations/0001_initial.py @@ -7,61 +7,103 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='CronTask', + name="CronTask", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('task_type', models.CharField(max_length=255)), - ('task_data', models.TextField(blank=True, null=True)), - ('added', models.DateTimeField(default=django.utils.timezone.now)), - ('run_at', models.DateTimeField(default=django.utils.timezone.now)), - ('email_to', models.EmailField(blank=True, max_length=254, null=True)), - ('email_subject', models.CharField(blank=True, max_length=255, null=True)), - ('email_html', models.TextField(blank=True, null=True)), - ('email_cc', models.CharField(blank=True, max_length=255, null=True)), - ('email_bcc', models.CharField(blank=True, max_length=255, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("task_type", models.CharField(max_length=255)), + ("task_data", models.TextField(blank=True, null=True)), + ("added", models.DateTimeField(default=django.utils.timezone.now)), + ("run_at", models.DateTimeField(default=django.utils.timezone.now)), + ("email_to", models.EmailField(blank=True, max_length=254, null=True)), + ( + "email_subject", + models.CharField(blank=True, max_length=255, null=True), + ), + ("email_html", models.TextField(blank=True, null=True)), + ("email_cc", models.CharField(blank=True, max_length=255, null=True)), + ("email_bcc", models.CharField(blank=True, max_length=255, null=True)), ], ), migrations.CreateModel( - name='Reminder', + name="Reminder", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('review', 'Review'), ('revisions', 'Revision')], max_length=100)), ( - 'run_type', + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.CharField( + choices=[("review", "Review"), ("revisions", "Revision")], + max_length=100, + ), + ), + ( + "run_type", models.CharField( choices=[ - ('before', 'before the due date'), - ('after', 'after the due date') + ("before", "before the due date"), + ("after", "after the due date"), ], - max_length=100 - ) + max_length=100, + ), ), ( - 'days', + "days", models.PositiveIntegerField( - help_text='The number of days before or after ' - 'the due date this reminder should fire' - ) + help_text="The number of days before or after " + "the due date this reminder should fire" + ), ), - ('template_name', models.CharField(help_text='The name of the email template', max_length=100)), - ('subject', models.CharField(max_length=200)), + ( + "template_name", + models.CharField( + help_text="The name of the email template", max_length=100 + ), + ), + ("subject", models.CharField(max_length=200)), ], ), migrations.CreateModel( - name='SentReminder', + name="SentReminder", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('review', 'Review'), ('revisions', 'Revision')], max_length=100)), - ('object_id', models.PositiveIntegerField()), - ('sent', models.DateField(default=django.utils.timezone.now)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.CharField( + choices=[("review", "Review"), ("revisions", "Revision")], + max_length=100, + ), + ), + ("object_id", models.PositiveIntegerField()), + ("sent", models.DateField(default=django.utils.timezone.now)), ], ), ] diff --git a/src/cron/migrations/0002_auto_20170711_1203.py b/src/cron/migrations/0002_auto_20170711_1203.py index dcb3b2f367..36c1182088 100755 --- a/src/cron/migrations/0002_auto_20170711_1203.py +++ b/src/cron/migrations/0002_auto_20170711_1203.py @@ -7,23 +7,29 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('journal', '0001_initial'), - ('cron', '0001_initial'), + ("journal", "0001_initial"), + ("cron", "0001_initial"), ] operations = [ migrations.AddField( - model_name='reminder', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="reminder", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), migrations.AddField( - model_name='crontask', - name='email_journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="crontask", + name="email_journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), ] diff --git a/src/cron/migrations/0003_auto_20171121_1115.py b/src/cron/migrations/0003_auto_20171121_1115.py index f4b746cd2d..75f5dd0c47 100755 --- a/src/cron/migrations/0003_auto_20171121_1115.py +++ b/src/cron/migrations/0003_auto_20171121_1115.py @@ -7,21 +7,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0017_auto_20171114_1502'), - ('cron', '0002_auto_20170711_1203'), + ("submission", "0017_auto_20171114_1502"), + ("cron", "0002_auto_20170711_1203"), ] operations = [ migrations.AddField( - model_name='crontask', - name='article', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="crontask", + name="article", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), ), migrations.AlterField( - model_name='reminder', - name='template_name', - field=models.CharField(help_text="The name of the email template, if it doesn't existyou will be asked to create it. Should have no spaces.", max_length=100), + model_name="reminder", + name="template_name", + field=models.CharField( + help_text="The name of the email template, if it doesn't existyou will be asked to create it. Should have no spaces.", + max_length=100, + ), ), ] diff --git a/src/cron/migrations/0004_auto_20210831_1159.py b/src/cron/migrations/0004_auto_20210831_1159.py index 0759e91330..a5f7842d9f 100644 --- a/src/cron/migrations/0004_auto_20210831_1159.py +++ b/src/cron/migrations/0004_auto_20210831_1159.py @@ -6,31 +6,44 @@ class Migration(migrations.Migration): - dependencies = [ - ('cron', '0003_auto_20171121_1115'), + ("cron", "0003_auto_20171121_1115"), ] operations = [ migrations.AlterField( - model_name='reminder', - name='template_name', + model_name="reminder", + name="template_name", field=models.CharField( help_text="The name of the email template. " - "If it does not exist, you will be " - "asked to create it. " - "Should have no spaces.", + "If it does not exist, you will be " + "asked to create it. " + "Should have no spaces.", max_length=100, ), ), migrations.AlterField( - model_name='reminder', - name='type', - field=models.CharField(choices=[('review', 'Review (Invited)'), ('accepted-review', 'Review (Accepted)'), ('revisions', 'Revision')], max_length=100), + model_name="reminder", + name="type", + field=models.CharField( + choices=[ + ("review", "Review (Invited)"), + ("accepted-review", "Review (Accepted)"), + ("revisions", "Revision"), + ], + max_length=100, + ), ), migrations.AlterField( - model_name='sentreminder', - name='type', - field=models.CharField(choices=[('review', 'Review (Invited)'), ('accepted-review', 'Review (Accepted)'), ('revisions', 'Revision')], max_length=100), + model_name="sentreminder", + name="type", + field=models.CharField( + choices=[ + ("review", "Review (Invited)"), + ("accepted-review", "Review (Accepted)"), + ("revisions", "Revision"), + ], + max_length=100, + ), ), ] diff --git a/src/cron/models.py b/src/cron/models.py index 3bb6d89ec5..f1efc5e13e 100755 --- a/src/cron/models.py +++ b/src/cron/models.py @@ -26,7 +26,7 @@ class CronTask(models.Model): added = models.DateTimeField(default=timezone.now) run_at = models.DateTimeField(default=timezone.now) article = models.ForeignKey( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.CASCADE, @@ -56,10 +56,19 @@ def run_tasks(): task.delete() @staticmethod - def add_email_task(to, subject, html, request, article=None, run_at=timezone.now(), cc=None, bcc=None): + def add_email_task( + to, + subject, + html, + request, + article=None, + run_at=timezone.now(), + cc=None, + bcc=None, + ): task = CronTask() - task.task_type = 'email_message' + task.task_type = "email_message" task.run_at = run_at task.email_to = to @@ -74,16 +83,16 @@ def add_email_task(to, subject, html, request, article=None, run_at=timezone.now REMINDER_CHOICES = ( - ('review', 'Review (Invited)'), - ('accepted-review', 'Review (Accepted)'), - ('revisions', 'Revision'), + ("review", "Review (Invited)"), + ("accepted-review", "Review (Accepted)"), + ("revisions", "Revision"), ) RUN_TYPE_CHOICES = ( # Before the event - ('before', 'before the due date'), + ("before", "before the due date"), # After the event - ('after', 'after the due date'), + ("after", "after the due date"), ) @@ -95,22 +104,22 @@ class SentReminder(models.Model): class Reminder(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) type = models.CharField(max_length=100, choices=REMINDER_CHOICES) run_type = models.CharField(max_length=100, choices=RUN_TYPE_CHOICES) days = models.PositiveIntegerField( help_text="The number of days before or " - "after the due date this " - "reminder should fire", + "after the due date this " + "reminder should fire", ) template_name = models.CharField( max_length=100, help_text="The name of the email template. " - "If it does not exist, you will be " - "asked to create it. " - "Should have no spaces.", + "If it does not exist, you will be " + "asked to create it. " + "Should have no spaces.", ) subject = models.CharField(max_length=200) @@ -134,9 +143,9 @@ def target_date(self): """ date_time = None - if self.run_type == 'before': + if self.run_type == "before": date_time = timezone.now() + timedelta(days=self.days) - elif self.run_type == 'after': + elif self.run_type == "after": date_time = timezone.now() - timedelta(days=self.days) if date_time: @@ -148,31 +157,39 @@ def items_for_reminder(self): model, objects, query = None, None, None from review import models as review_models - if self.type == 'review': + if self.type == "review": model = review_models.ReviewAssignment - query = (Q(date_declined__isnull=True) & - Q(date_complete__isnull=True) & - Q(date_accepted__isnull=True) & - Q(article__stage__in=submission_models.REVIEW_STAGES)) - elif self.type == 'accepted-review': + query = ( + Q(date_declined__isnull=True) + & Q(date_complete__isnull=True) + & Q(date_accepted__isnull=True) + & Q(article__stage__in=submission_models.REVIEW_STAGES) + ) + elif self.type == "accepted-review": model = review_models.ReviewAssignment - query = (Q(date_declined__isnull=True) & - Q(date_complete__isnull=True) & - Q(date_accepted__isnull=False) & - Q(article__stage__in=submission_models.REVIEW_STAGES)) - elif self.type == 'revisions': + query = ( + Q(date_declined__isnull=True) + & Q(date_complete__isnull=True) + & Q(date_accepted__isnull=False) + & Q(article__stage__in=submission_models.REVIEW_STAGES) + ) + elif self.type == "revisions": model = review_models.RevisionRequest - query = (Q(date_completed__isnull=True) & - Q(article__stage__in=submission_models.REVIEW_STAGES)) + query = Q(date_completed__isnull=True) & Q( + article__stage__in=submission_models.REVIEW_STAGES + ) target_date = self.target_date() if target_date: - objects = model.objects.filter(date_due=target_date).filter(query, article__journal=self.journal) + objects = model.objects.filter(date_due=target_date).filter( + query, article__journal=self.journal + ) return objects def send_reminder(self, test=False): from review import models as review_models + objects = self.items_for_reminder() request = Request() request.journal = self.journal @@ -187,29 +204,31 @@ def send_reminder(self, test=False): ) # Create context early so qw can add the correct variable - context = {'journal': self.journal, 'article': item.article} + context = {"journal": self.journal, "article": item.article} # Check if the item is a ReviewAssignment or RevisionRequest if isinstance(item, review_models.ReviewAssignment): to = item.reviewer.email - context['review_assignment'] = item + context["review_assignment"] = item # get the review_url for the email context review_url = review_logic.get_review_url(request, item) - context['review_url'] = review_url + context["review_url"] = review_url elif isinstance(item, review_models.RevisionRequest): to = item.article.correspondence_author.email - context['revision'] = item + context["revision"] = item # get the do_revisions url - do_revisions_url = request.journal.site_url(path=reverse( - 'do_revisions', - kwargs={ - 'article_id': item.article.pk, - 'revision_id': item.pk, - } - )) - context['do_revisions_url'] = do_revisions_url + do_revisions_url = request.journal.site_url( + path=reverse( + "do_revisions", + kwargs={ + "article_id": item.article.pk, + "revision_id": item.pk, + }, + ) + ) + context["do_revisions_url"] = do_revisions_url if not test and not sent_check and to: message = render_template.get_requestless_content( @@ -218,11 +237,11 @@ def send_reminder(self, test=False): self.template_name, ) log_dict = { - 'level': 'Info', - 'action_text': 'Automated reminder sent', - 'types': 'Automated Reminder', - 'target': item.article, - 'actor': None, + "level": "Info", + "action_text": "Automated reminder sent", + "types": "Automated Reminder", + "target": item.article, + "actor": None, } notify_helpers.send_email_with_body_from_user( request, @@ -234,11 +253,17 @@ def send_reminder(self, test=False): # Create a SentReminder object to ensure we don't do this more # than once by accident. SentReminder.objects.create(type=self.type, object_id=item.pk) - logger.info('Reminder sent for {0}'.format(item)) + logger.info("Reminder sent for {0}".format(item)) elif test: - logger.info("[TEST] reminder for {} due on {}".format(item, item.date_due)) + logger.info( + "[TEST] reminder for {} due on {}".format(item, item.date_due) + ) else: - logger.info('Reminder {0} for object {1} has already been sent'.format(self, item)) + logger.info( + "Reminder {0} for object {1} has already been sent".format( + self, item + ) + ) class Request(object): diff --git a/src/cron/tests/test_commands.py b/src/cron/tests/test_commands.py index 8e4df6d97d..f28bf932aa 100644 --- a/src/cron/tests/test_commands.py +++ b/src/cron/tests/test_commands.py @@ -5,23 +5,25 @@ from cron import forms, models from django.utils import timezone + class SendRemindersCommandTests(TestCase): """ Unit tests for commands. """ + @classmethod def setUpTestData(cls): - cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() - cls.review_assignment = helpers.create_review_assignment(journal=cls.journal_one) + cls.review_assignment = helpers.create_review_assignment( + journal=cls.journal_one + ) cls.review_reminder = helpers.create_reminder( - journal=cls.journal_one, - reminder_type='review' + journal=cls.journal_one, reminder_type="review" ) def test_handle(self): - call_command('send_reminders') + call_command("send_reminders") reminder_item = self.review_reminder.items_for_reminder()[0] sent_check = models.SentReminder.objects.filter( type=self.review_reminder.type, diff --git a/src/cron/tests/test_models.py b/src/cron/tests/test_models.py index 1fb43a965d..64c36d5650 100644 --- a/src/cron/tests/test_models.py +++ b/src/cron/tests/test_models.py @@ -3,21 +3,26 @@ from utils.testing import helpers from cron import forms, models + class ModelTests(TestCase): """ Unit tests for commands. """ + @classmethod def setUpTestData(cls): - cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() - cls.review_assignment = helpers.create_review_assignment(journal=cls.journal_one) + cls.review_assignment = helpers.create_review_assignment( + journal=cls.journal_one + ) cls.review_reminder = helpers.create_reminder( journal=cls.journal_one, - reminder_type='review', + reminder_type="review", + ) + cls.review_assignment = helpers.create_review_assignment( + journal=cls.journal_two ) - cls.review_assignment = helpers.create_review_assignment(journal=cls.journal_two) def test_items_for_reminder(self): self.assertEqual(1, len(self.review_reminder.items_for_reminder())) diff --git a/src/cron/urls.py b/src/cron/urls.py index f894253386..5b743bd6f3 100755 --- a/src/cron/urls.py +++ b/src/cron/urls.py @@ -7,12 +7,18 @@ from cron import views urlpatterns = [ - re_path(r'^$', views.home, name='cron_home'), - re_path(r'^reminders/$', views.reminders_index, name='cron_reminders'), - re_path(r'^reminders/new/$', views.manage_reminder, name='cron_create_reminder'), - re_path(r'^reminders/(?P\d+)/$', views.manage_reminder, name='cron_reminder'), - re_path(r'^reminders/(?P\d+)/template/(?P.*)/$', views.create_template, - name='cron_create_template'), - - re_path(r'^readers/$', views.readers_index, name='cron_readers'), + re_path(r"^$", views.home, name="cron_home"), + re_path(r"^reminders/$", views.reminders_index, name="cron_reminders"), + re_path(r"^reminders/new/$", views.manage_reminder, name="cron_create_reminder"), + re_path( + r"^reminders/(?P\d+)/$", + views.manage_reminder, + name="cron_reminder", + ), + re_path( + r"^reminders/(?P\d+)/template/(?P.*)/$", + views.create_template, + name="cron_create_template", + ), + re_path(r"^readers/$", views.readers_index, name="cron_readers"), ] diff --git a/src/cron/views.py b/src/cron/views.py index 8aadd93169..2f84e60aca 100755 --- a/src/cron/views.py +++ b/src/cron/views.py @@ -33,15 +33,17 @@ def reminders_index(request): """ reminders = models.Reminder.objects.filter(journal=request.journal) - if 'delete' in request.POST: - reminder_id = request.POST.get('delete') - reminder = get_object_or_404(models.Reminder, pk=reminder_id, journal=request.journal) + if "delete" in request.POST: + reminder_id = request.POST.get("delete") + reminder = get_object_or_404( + models.Reminder, pk=reminder_id, journal=request.journal + ) reminder.delete() - return redirect(reverse('cron_reminders')) + return redirect(reverse("cron_reminders")) - template = 'cron/reminders.html' + template = "cron/reminders.html" context = { - 'reminders': reminders, + "reminders": reminders, } return render(request, template, context) @@ -57,7 +59,9 @@ def manage_reminder(request, reminder_id=None): """ reminder = None if reminder_id: - reminder = get_object_or_404(models.Reminder, journal=request.journal, pk=reminder_id) + reminder = get_object_or_404( + models.Reminder, journal=request.journal, pk=reminder_id + ) reminders = models.Reminder.objects.filter(journal=request.journal) form = forms.ReminderForm( @@ -77,22 +81,23 @@ def manage_reminder(request, reminder_id=None): # Check if the template exists and redirect accordingly. check_template = logic.check_template_exists(request, reminder) if check_template: - return redirect(reverse('cron_reminders')) + return redirect(reverse("cron_reminders")) else: return redirect( reverse( - 'cron_create_template', + "cron_create_template", kwargs={ - 'reminder_id': reminder.pk, 'template_name': reminder.template_name - } + "reminder_id": reminder.pk, + "template_name": reminder.template_name, + }, ) ) - template = 'cron/manage_reminder.html' + template = "cron/manage_reminder.html" context = { - 'reminder': reminder, - 'reminders': reminders, - 'form': form, + "reminder": reminder, + "reminders": reminders, + "form": form, } return render(request, template, context) @@ -107,25 +112,36 @@ def create_template(request, reminder_id, template_name): :param template_name: string, Reminder.template string :return: if POST HttpRedirect otherwise HttpResponse """ - reminder = get_object_or_404(models.Reminder, journal=request.journal, pk=reminder_id) + reminder = get_object_or_404( + models.Reminder, journal=request.journal, pk=reminder_id + ) try: - text = setting_handler.get_setting('email', template_name, request.journal).value + text = setting_handler.get_setting( + "email", template_name, request.journal + ).value except core_models.Setting.DoesNotExist: - text = '' + text = "" if request.POST: - template = request.POST.get('template') - setting_handler.create_setting('email', template_name, 'rich-text', reminder.subject, '', is_translatable=True) - setting_handler.save_setting('email', template_name, request.journal, template) + template = request.POST.get("template") + setting_handler.create_setting( + "email", + template_name, + "rich-text", + reminder.subject, + "", + is_translatable=True, + ) + setting_handler.save_setting("email", template_name, request.journal, template) - return redirect(reverse('cron_reminders')) + return redirect(reverse("cron_reminders")) - template = 'cron/create_template.html' + template = "cron/create_template.html" context = { - 'reminder': reminder, - 'template_name': template_name, - 'text': text, + "reminder": reminder, + "template_name": template_name, + "text": text, } return render(request, template, context) @@ -134,25 +150,22 @@ def create_template(request, reminder_id, template_name): @has_journal @editor_user_required def readers_index(request): - switch_value = setting_handler.get_setting( - 'notifications', - 'send_reader_notifications', - request.journal + "notifications", "send_reader_notifications", request.journal ).value - readers = request.journal.users_with_role('reader') + readers = request.journal.users_with_role("reader") content_type = ContentType.objects.get_for_model(request.journal) reader_notifications = utils_models.LogEntry.objects.filter( - types='Publication Notification', + types="Publication Notification", content_type=content_type, object_id=request.journal.pk, ) - template = 'admin/cron/readers.html' + template = "admin/cron/readers.html" context = { - 'send_reader_notifications': switch_value, - 'readers': readers, - 'reader_notifications': reader_notifications, + "send_reader_notifications": switch_value, + "readers": readers, + "reader_notifications": reader_notifications, } return render( request, diff --git a/src/discussion/admin.py b/src/discussion/admin.py index f67386a5a5..3b17264ba1 100644 --- a/src/discussion/admin.py +++ b/src/discussion/admin.py @@ -10,34 +10,47 @@ class ThreadAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', 'subject', 'owner', 'started', 'object_title', - '_journal') - list_filter = ('article__journal', 'subject', 'started', 'last_updated') - search_fields = ('pk', 'article__title', 'preprint__title', - 'subject', 'owner__first_name', 'owner__last_name', - 'owner__email', 'post__body') - raw_id_fields = ('owner', 'article', 'preprint') - - inlines = [ - admin_utils.PostInline - ] + list_display = ("pk", "subject", "owner", "started", "object_title", "_journal") + list_filter = ("article__journal", "subject", "started", "last_updated") + search_fields = ( + "pk", + "article__title", + "preprint__title", + "subject", + "owner__first_name", + "owner__last_name", + "owner__email", + "post__body", + ) + raw_id_fields = ("owner", "article", "preprint") + + inlines = [admin_utils.PostInline] class PostAdmin(admin.ModelAdmin): - list_display = ('_post', 'thread', 'owner', 'posted', '_journal') - list_filter = ('thread__article__journal', 'posted') - search_fields = ('pk', 'body', 'thread__subject', 'owner__first_name', - 'owner__last_name', 'owner__email',) - raw_id_fields = ('thread', 'owner',) - filter_horizontal = ('read_by',) + list_display = ("_post", "thread", "owner", "posted", "_journal") + list_filter = ("thread__article__journal", "posted") + search_fields = ( + "pk", + "body", + "thread__subject", + "owner__first_name", + "owner__last_name", + "owner__email", + ) + raw_id_fields = ( + "thread", + "owner", + ) + filter_horizontal = ("read_by",) save_as = True - date_hierarchy = ('posted') + date_hierarchy = "posted" def _post(self, obj): - return truncatewords_html(obj.body, 10) if obj else '' + return truncatewords_html(obj.body, 10) if obj else "" def _journal(self, obj): - return obj.thread.article.journal if obj else '' + return obj.thread.article.journal if obj else "" admin_list = [ diff --git a/src/discussion/apps.py b/src/discussion/apps.py index e5fc245747..15948cba78 100644 --- a/src/discussion/apps.py +++ b/src/discussion/apps.py @@ -2,4 +2,4 @@ class DiscussionConfig(AppConfig): - name = 'discussion' + name = "discussion" diff --git a/src/discussion/forms.py b/src/discussion/forms.py index 86f1672cb6..b1d4562f1d 100644 --- a/src/discussion/forms.py +++ b/src/discussion/forms.py @@ -6,20 +6,18 @@ class ThreadForm(forms.ModelForm): class Meta: model = models.Thread - fields = ( - 'subject', - ) + fields = ("subject",) def __init__(self, *args, **kwargs): - self.object = kwargs.pop('object') - self.object_type = kwargs.pop('object_type') - self.owner = kwargs.pop('owner') + self.object = kwargs.pop("object") + self.object_type = kwargs.pop("object_type") + self.owner = kwargs.pop("owner") super(ThreadForm, self).__init__(*args, **kwargs) def save(self, commit=True): thread = super(ThreadForm, self).save(commit=False) - if self.object_type == 'article': + if self.object_type == "article": thread.article = self.object else: thread.preprint = self.object diff --git a/src/discussion/migrations/0001_initial.py b/src/discussion/migrations/0001_initial.py index 6273ab980c..58ab372ca3 100644 --- a/src/discussion/migrations/0001_initial.py +++ b/src/discussion/migrations/0001_initial.py @@ -9,45 +9,97 @@ class Migration(migrations.Migration): - initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('submission', '0047_merge_20200626_1336'), - ('repository', '0009_auto_20200820_1212'), + ("submission", "0047_merge_20200626_1336"), + ("repository", "0009_auto_20200820_1212"), ] operations = [ migrations.CreateModel( - name='Post', + name="Post", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('posted', models.DateTimeField(default=django.utils.timezone.now)), - ('body', models.TextField()), - ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("posted", models.DateTimeField(default=django.utils.timezone.now)), + ("body", models.TextField()), + ( + "owner", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('posted', 'pk'), + "ordering": ("posted", "pk"), }, ), migrations.CreateModel( - name='Thread', + name="Thread", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('subject', models.CharField(help_text='Max. 300 characters.', max_length=300)), - ('started', models.DateTimeField(default=django.utils.timezone.now)), - ('article', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Article')), - ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ('preprint', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "subject", + models.CharField(help_text="Max. 300 characters.", max_length=300), + ), + ("started", models.DateTimeField(default=django.utils.timezone.now)), + ( + "article", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Article", + ), + ), + ( + "owner", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "preprint", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Preprint", + ), + ), ], options={ - 'ordering': ('started', 'pk'), + "ordering": ("started", "pk"), }, ), migrations.AddField( - model_name='post', - name='thread', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='discussion.Thread'), + model_name="post", + name="thread", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="discussion.Thread", + ), ), ] diff --git a/src/discussion/migrations/0002_auto_20200820_1400.py b/src/discussion/migrations/0002_auto_20200820_1400.py index acc1461d65..4146f0c48c 100644 --- a/src/discussion/migrations/0002_auto_20200820_1400.py +++ b/src/discussion/migrations/0002_auto_20200820_1400.py @@ -8,21 +8,25 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('discussion', '0001_initial'), + ("discussion", "0001_initial"), ] operations = [ migrations.AddField( - model_name='post', - name='read_by', + model_name="post", + name="read_by", field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), ), migrations.AlterField( - model_name='post', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='posts', to=settings.AUTH_USER_MODEL), + model_name="post", + name="owner", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="posts", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/discussion/migrations/0003_auto_20200821_1422.py b/src/discussion/migrations/0003_auto_20200821_1422.py index acf9dbc8a2..944d6a66c7 100644 --- a/src/discussion/migrations/0003_auto_20200821_1422.py +++ b/src/discussion/migrations/0003_auto_20200821_1422.py @@ -7,23 +7,22 @@ class Migration(migrations.Migration): - dependencies = [ - ('discussion', '0002_auto_20200820_1400'), + ("discussion", "0002_auto_20200820_1400"), ] operations = [ migrations.AlterModelOptions( - name='post', - options={'ordering': ('-posted', 'pk')}, + name="post", + options={"ordering": ("-posted", "pk")}, ), migrations.AlterModelOptions( - name='thread', - options={'ordering': ('-started', 'pk')}, + name="thread", + options={"ordering": ("-started", "pk")}, ), migrations.AddField( - model_name='thread', - name='last_updated', + model_name="thread", + name="last_updated", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/src/discussion/migrations/0004_auto_20200925_1933.py b/src/discussion/migrations/0004_auto_20200925_1933.py index 5df3ef79ba..e714c2cd10 100644 --- a/src/discussion/migrations/0004_auto_20200925_1933.py +++ b/src/discussion/migrations/0004_auto_20200925_1933.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('discussion', '0003_auto_20200821_1422'), + ("discussion", "0003_auto_20200821_1422"), ] operations = [ migrations.AlterModelOptions( - name='thread', - options={'ordering': ('-last_updated', 'pk')}, + name="thread", + options={"ordering": ("-last_updated", "pk")}, ), ] diff --git a/src/discussion/models.py b/src/discussion/models.py index e368a100e3..c014f4c540 100644 --- a/src/discussion/models.py +++ b/src/discussion/models.py @@ -6,25 +6,25 @@ class Thread(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.SET_NULL, ) preprint = models.ForeignKey( - 'repository.Preprint', + "repository.Preprint", blank=True, null=True, on_delete=models.SET_NULL, ) owner = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) subject = models.CharField( max_length=300, - help_text=_('Max. 300 characters.'), + help_text=_("Max. 300 characters."), ) started = models.DateTimeField( default=timezone.now, @@ -34,7 +34,7 @@ class Thread(models.Model): ) class Meta: - ordering = ('-last_updated', 'pk') + ordering = ("-last_updated", "pk") def __str__(self): return self.subject @@ -47,9 +47,9 @@ def object_title(self): def object_string(self): if self.article: - return 'article' + return "article" elif self.preprint: - return 'preprint' + return "preprint" def object_id(self): if self.article: @@ -76,22 +76,22 @@ class Post(models.Model): on_delete=models.SET_NULL, ) owner = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, - related_name='posts', + related_name="posts", ) posted = models.DateTimeField( default=timezone.now, ) body = models.TextField() read_by = models.ManyToManyField( - 'core.Account', + "core.Account", blank=True, ) class Meta: - ordering = ('-posted', 'pk') + ordering = ("-posted", "pk") def user_has_read(self, user): if user in self.read_by.all(): @@ -100,6 +100,6 @@ def user_has_read(self, user): def display_date(self): if self.posted.date() == timezone.now().date(): - return 'Today' + return "Today" else: return self.posted.date() diff --git a/src/discussion/urls.py b/src/discussion/urls.py index cc098987d8..d2a4c1f9e7 100644 --- a/src/discussion/urls.py +++ b/src/discussion/urls.py @@ -7,7 +7,19 @@ from discussion import views urlpatterns = [ - re_path(r'^(?Ppreprint|article)/(?P\d+)/$', views.threads, name='discussion_threads'), - re_path(r'^(?Ppreprint|article)/(?P\d+)/thread/(?P\d+)/$', views.threads, name='discussion_thread'), - re_path(r'^thread/(?P\d+)/post/new/$', views.add_post, name='discussion_add_post'), + re_path( + r"^(?Ppreprint|article)/(?P\d+)/$", + views.threads, + name="discussion_threads", + ), + re_path( + r"^(?Ppreprint|article)/(?P\d+)/thread/(?P\d+)/$", + views.threads, + name="discussion_thread", + ), + re_path( + r"^thread/(?P\d+)/post/new/$", + views.add_post, + name="discussion_add_post", + ), ] diff --git a/src/discussion/views.py b/src/discussion/views.py index dc23b7c96f..4e73fd3d99 100644 --- a/src/discussion/views.py +++ b/src/discussion/views.py @@ -15,7 +15,7 @@ def threads(request, object_type, object_id, thread_id=None): """ modal = None - if object_type == 'article': + if object_type == "article": object_to_get = get_object_or_404( submission_models.Article, pk=object_id, @@ -59,25 +59,25 @@ def threads(request, object_type, object_id, thread_id=None): thread = form.save() return redirect( reverse( - 'discussion_thread', + "discussion_thread", kwargs={ - 'object_type': thread.object_string(), - 'object_id': thread.object_id(), - 'thread_id': thread.pk, - } + "object_type": thread.object_string(), + "object_id": thread.object_id(), + "thread_id": thread.pk, + }, ) ) else: - modal = 'new_thread' + modal = "new_thread" - template = 'admin/discussion/threads.html' + template = "admin/discussion/threads.html" context = { - 'object': object_to_get, - 'object_type': object_type, - 'threads': threads, - 'active_thread': thread, - 'form': form, - 'modal': modal, + "object": object_to_get, + "object_type": object_type, + "threads": threads, + "active_thread": thread, + "form": form, + "modal": modal, } return render(request, template, context) @@ -91,17 +91,15 @@ def add_post(request, thread_id): ) thread.create_post( request.user, - request.POST.get('new_post'), + request.POST.get("new_post"), ) return redirect( reverse( - 'discussion_thread', + "discussion_thread", kwargs={ - 'object_type': thread.object_string(), - 'object_id': thread.object_id(), - 'thread_id': thread.pk, - } + "object_type": thread.object_string(), + "object_id": thread.object_id(), + "thread_id": thread.pk, + }, ) ) - - diff --git a/src/events/logic.py b/src/events/logic.py index 13dfa72e81..678ae5f23c 100755 --- a/src/events/logic.py +++ b/src/events/logic.py @@ -12,277 +12,278 @@ class Events: """ The event handling framework for Janeway """ + _hooks = {} # list of event constants used internally by Janeway # kwargs: article, request # raised when an article submission has started - ON_ARTICLE_SUBMISSION_START = 'on_article_submission_start' + ON_ARTICLE_SUBMISSION_START = "on_article_submission_start" # kwargs: article, request # raised when an article is submitted - ON_ARTICLE_SUBMITTED = 'on_article_submitted' + ON_ARTICLE_SUBMITTED = "on_article_submitted" # kwargs: editor_assignment, request, email_data, acknowledgement (true), skip (boolean) # raised when an editor is manually assigned to an article(or skip the acknowledgement) - ON_EDITOR_MANUALLY_ASSIGNED = 'on_editor_manually_assigned' + ON_EDITOR_MANUALLY_ASSIGNED = "on_editor_manually_assigned" # kwargs: request, editor_assignment, user_message_content (will be blank), acknowledgement (false) # raised when an editor is assigned to an article - ON_ARTICLE_ASSIGNED = 'on_article_assigned' + ON_ARTICLE_ASSIGNED = "on_article_assigned" # kwargs: request, editor_assignment, user_message_content, skip (boolean) # raised when an editor is unassigned from an article - ON_ARTICLE_UNASSIGNED = 'on_article_unassigned' + ON_ARTICLE_UNASSIGNED = "on_article_unassigned" # kwargs: editor_assignment, request, user_message_content, acknowledgement (true), skip (boolean) # raised when an editor decides to notify the editor of the assignment (or skip the acknowledgement) - ON_ARTICLE_ASSIGNED_ACKNOWLEDGE = 'on_article_assigned_acknowledge' + ON_ARTICLE_ASSIGNED_ACKNOWLEDGE = "on_article_assigned_acknowledge" # kwargs: review_assignment, request, user_message_content (will be blank), acknowledgement (false) # raised when a review is requested - ON_REVIEWER_REQUESTED = 'on_reviewer_requested' + ON_REVIEWER_REQUESTED = "on_reviewer_requested" # kwargs: review_assignment, request, email_data, acknowledgement (true), skip (boolean) # raised when an editor decides to notify the reviewer with a custom message or skipped the email - ON_REVIEWER_REQUESTED_NOTIFICATION = 'on_reviewer_requested_notification' + ON_REVIEWER_REQUESTED_NOTIFICATION = "on_reviewer_requested_notification" # kwargs: review_assignment, request, user_message_content, acknowledgement (true), skip (boolean) # raised when an editor decides to notify the reviewer of the request (or skip the acknowledgement) - ON_REVIEWER_REQUESTED_ACKNOWLEDGE = 'on_reviewer_requested_acknowledge' + ON_REVIEWER_REQUESTED_ACKNOWLEDGE = "on_reviewer_requested_acknowledge" # kwargs: review_assignment, request, user_message_content, skip (boolean) # raised when an editor decides to notify the reviewer of a assignment withdrawl (or skip the notification) - ON_REVIEW_WITHDRAWL = 'on_review_withdrawl' + ON_REVIEW_WITHDRAWL = "on_review_withdrawl" # kwargs: review_assignment, request, accepted (boolean) # raised when a reviewer accepts or declines to review - ON_REVIEWER_ACCEPTED = 'on_reviewer_accepted' - ON_REVIEWER_DECLINED = 'on_reviewer_declined' + ON_REVIEWER_ACCEPTED = "on_reviewer_accepted" + ON_REVIEWER_DECLINED = "on_reviewer_declined" # kwargs: review_assignment, request # raised when a reviewer completes his or her review - ON_REVIEW_COMPLETE = 'on_review_complete' + ON_REVIEW_COMPLETE = "on_review_complete" # kwargs: review_assignment, request # raised when a reviewer completes his or her review - ON_REVIEW_CLOSED = 'on_review_closed' + ON_REVIEW_CLOSED = "on_review_closed" # kwargs: article, request, user_message_content, decision, skip (boolean) # raised when an editor accepts or declines an article - ON_ARTICLE_DECLINED = 'on_article_declined' + ON_ARTICLE_DECLINED = "on_article_declined" # kwargs: article, request, user_message_content, decision, skip (boolean) # raised when an editor undeclines an article - ON_ARTICLE_UNDECLINED = 'on_article_undeclined' + ON_ARTICLE_UNDECLINED = "on_article_undeclined" # kwargs: article, request, user_message_content, decision, skip (boolean) # raised when an editor accepts or accepts an article - ON_ARTICLE_ACCEPTED = 'on_article_accepted' + ON_ARTICLE_ACCEPTED = "on_article_accepted" # kwargs: article, revision, request, user_message_content, skip (boolean) # raised when a revision request is created - ON_REVISIONS_REQUESTED = 'on_revisions_requested' + ON_REVISIONS_REQUESTED = "on_revisions_requested" # kwargs: article, revision, request, user_message_content, skip (boolean) # raised when a revision request notification is sent - ON_REVISIONS_REQUESTED_NOTIFY = 'on_revisions_requested_notify' + ON_REVISIONS_REQUESTED_NOTIFY = "on_revisions_requested_notify" # kwargs: article, revision, request, skip (boolean) # raised when a revision request notification is sent - ON_REVISIONS_COMPLETE = 'on_revisions_complete' + ON_REVISIONS_COMPLETE = "on_revisions_complete" # kwargs: article, draft, request # raised when a section editor adds a new draft - ON_DRAFT_DECISION = 'on_draft_decision' + ON_DRAFT_DECISION = "on_draft_decision" # kwargs: article, request, decision, skip (boolean) # raised when an editor declines a draft decision - ON_DRAFT_DECISION_DECLINED = 'on_draft_decision_declined' + ON_DRAFT_DECISION_DECLINED = "on_draft_decision_declined" # kwargs: article, copyeditor_assignment, request, skip (boolean) # raised when a copyeditor is assigned - ON_COPYEDIT_ASSIGNMENT = 'on_copyedit_assignment' + ON_COPYEDIT_ASSIGNMENT = "on_copyedit_assignment" # kwargs: copyeditor_assignment, request, skip (boolean) # raised when a copyeditor assignment is updated - ON_COPYEDIT_UPDATED = 'on_copyedit_updated' + ON_COPYEDIT_UPDATED = "on_copyedit_updated" # kwargs: article, request - ON_COPYEDIT_DELETED = 'on_copyedit_deleted' + ON_COPYEDIT_DELETED = "on_copyedit_deleted" # kwargs copyeditor assignment, decision, request # raised when a copyeditor accepts or declines a task - ON_COPYEDITOR_DECISION = 'on_copyeditor_decision' + ON_COPYEDITOR_DECISION = "on_copyeditor_decision" # kwargs: article, copyeditor assignment, request # raised when a copyedit assignment is complete - ON_COPYEDIT_ASSIGNMENT_COMPLETE = 'on_copyedit_assignment_complete' + ON_COPYEDIT_ASSIGNMENT_COMPLETE = "on_copyedit_assignment_complete" # kwargs: article, copyeditor assignment, request, skip (boolean) # raised when an author is invited to review a copyedit - ON_COPYEDIT_AUTHOR_REVIEW = 'on_copyedit_author_review' + ON_COPYEDIT_AUTHOR_REVIEW = "on_copyedit_author_review" # kwargs: article, copyedit, author_review, request # raised when an author completes their copyedit review - ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE = 'on_copyedit_author_review_complete' + ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE = "on_copyedit_author_review_complete" # kwargs author_review, request # raised when an author review is deleted - ON_COPYEDIT_AUTHOR_REVIEW_DELETED = 'on_copyedit_author_review_delete' + ON_COPYEDIT_AUTHOR_REVIEW_DELETED = "on_copyedit_author_review_delete" # kwargs: article, copyeditor assignment, request, skip (boolean) # raised when a copyedit assignment is acknowledged - ON_COPYEDIT_ACKNOWLEDGE = 'on_copyedit_acknowledge' + ON_COPYEDIT_ACKNOWLEDGE = "on_copyedit_acknowledge" # kwargs: article, copyeditor assignment, request, skip (boolean) # raised when a copyedit assignment is acknowledged - ON_COPYEDIT_REOPEN = 'on_copyedit_reopen' + ON_COPYEDIT_REOPEN = "on_copyedit_reopen" # kwargs: article, request # raised when copyediting is complete - ON_COPYEDIT_COMPLETE = 'on_copyedit_complete' + ON_COPYEDIT_COMPLETE = "on_copyedit_complete" # kwargs: production_assignment, request, skip (boolean) # raised when a production manager is assigned to an article - ON_PRODUCTION_MANAGER_ASSIGNMENT = 'on_production_manager_assignment' + ON_PRODUCTION_MANAGER_ASSIGNMENT = "on_production_manager_assignment" # kwargs: user_content_message, typeset_task, request, skip (boolean) # raised when a typeset task is assigned to a typesetter - ON_TYPESET_TASK_ASSIGNED = 'on_typeset_task_assigned' + ON_TYPESET_TASK_ASSIGNED = "on_typeset_task_assigned" # kwargs: typeset_task, decision, request # raised when a typesetter accepts or declines a typeset task - ON_TYPESETTER_DECISION = 'on_typesetter_decision' + ON_TYPESETTER_DECISION = "on_typesetter_decision" # kwargs: typeset_task, request # raised when an editor deletes a typesetter's task - ON_TYPESET_TASK_DELETED = 'on_typeset_task_deleted' + ON_TYPESET_TASK_DELETED = "on_typeset_task_deleted" # kwargs: typeset_task, request # raised when a typesetter completes their task - ON_TYPESET_COMPLETE = 'on_typeset_complete' + ON_TYPESET_COMPLETE = "on_typeset_complete" # kwargs: typeset_task, request, skip(boolean) # raised when a typesetter is sent a second request - ON_TYPESET_TASK_REOPENED = 'on_typeset_task_reopened' + ON_TYPESET_TASK_REOPENED = "on_typeset_task_reopened" # kwargs: typeset_task, request, skip (boolean) # raised when a production manager accepts a typeset task and completed the production stage - ON_TYPESET_ACK = 'on_typeset_ack' + ON_TYPESET_ACK = "on_typeset_ack" # kwargs: request, article, skip (boolean) # raised when a production manager completes the production stage - ON_PRODUCTION_COMPLETE = 'on_production_complete' + ON_PRODUCTION_COMPLETE = "on_production_complete" # kwargs: proofing_assignment, request, skip (boolean) # raised when a production manager is assigned to an article - ON_PROOFING_MANAGER_ASSIGNMENT = 'on_proofing_manager_assignment' + ON_PROOFING_MANAGER_ASSIGNMENT = "on_proofing_manager_assignment" # kwargs: request, article, proofing_task, skip (boolean) # raised when a proofing manager invites a Proofreader - ON_NOTIFY_PROOFREADER = 'on_notify_proofreader' + ON_NOTIFY_PROOFREADER = "on_notify_proofreader" # kwargs: request, article, proofing_task, # raised when a proofing manager cancels a proofing task - ON_CANCEL_PROOFING_TASK = 'on_cancel_proofing_task' + ON_CANCEL_PROOFING_TASK = "on_cancel_proofing_task" # kwargs: request, article, proofing_task # raised when a proofing manager cancels a proofing task - ON_EDIT_PROOFING_TASK = 'on_edit_proofing_task' + ON_EDIT_PROOFING_TASK = "on_edit_proofing_task" # kwargs: request, proofing_task, decision # raised when a proofreader accepts or declines a task - ON_PROOFREADER_TASK_DECISION = 'on_proofreader_task_decision' + ON_PROOFREADER_TASK_DECISION = "on_proofreader_task_decision" # kwargs: request, proofing_task, article # raised when a proofing task is completed - ON_COMPLETE_PROOFING_TASK = 'on_complete_proofing_task' + ON_COMPLETE_PROOFING_TASK = "on_complete_proofing_task" # kwargs: request, typeset_task, user_content_message, skip (boolean) # raised when a proofing manager requests changes from a typesetter. - ON_PROOFING_TYPESET_CHANGES_REQUEST = 'on_proofing_typeset_changes_request' + ON_PROOFING_TYPESET_CHANGES_REQUEST = "on_proofing_typeset_changes_request" # kwargs: request, proofing_task, decision # raised when a typesetter accepts a proofing change request - ON_PROOFING_TYPESET_DECISION = 'on_proofing_typeset_decision' + ON_PROOFING_TYPESET_DECISION = "on_proofing_typeset_decision" # kwargs: request, correction_task, article # raised when a proofing manager cancels/deletes a correction task - ON_CORRECTIONS_CANCELLED = 'on_corrections_cancelled' + ON_CORRECTIONS_CANCELLED = "on_corrections_cancelled" # kwargs: request, proofing_task, article # raised when a typesetter completes corrections - ON_CORRECTIONS_COMPLETE = 'on_corrections_complete' + ON_CORRECTIONS_COMPLETE = "on_corrections_complete" # kwargs: request, article, user_message, model_object, model_name # raised when a Proofing Manager acks a proofing task - ON_PROOFING_ACK = 'on_proofing_ack' + ON_PROOFING_ACK = "on_proofing_ack" # kwargs: request, article, user_message # raised when proofing is complete - ON_PROOFING_COMPLETE = 'on_proofing_complete' + ON_PROOFING_COMPLETE = "on_proofing_complete" # kwargs: article, issue, user # raised when an article is assigned to an issue - ON_ARTICLE_ASSIGNED_TO_ISSUE = 'on_article_assigned_to_issue' + ON_ARTICLE_ASSIGNED_TO_ISSUE = "on_article_assigned_to_issue" # kwargs: request, article # raised when an article is marked as published - ON_ARTICLE_PUBLISHED = 'on_article_published' + ON_ARTICLE_PUBLISHED = "on_article_published" # kwargs: request, article # raised when an Editor notifies an author that publication is set - ON_AUTHOR_PUBLICATION = 'on_author_publication' + ON_AUTHOR_PUBLICATION = "on_author_publication" # kwargs: request, article, notification_formset # raised when an editor notifies author, editors, # and/or reviewers that publication is set - ON_PREPUB_NOTIFICATIONS = 'on_prepub_notifications' + ON_PREPUB_NOTIFICATIONS = "on_prepub_notifications" # kwargs: request, override # raised when an Editor overrides review security - ON_REVIEW_SECURITY_OVERRIDE = 'on_review_security_override' + ON_REVIEW_SECURITY_OVERRIDE = "on_review_security_override" # kwargs: request, article # raised when a new preprint article is submitted - ON_PREPRINT_SUBMISSION = 'on_preprint_submission' + ON_PREPRINT_SUBMISSION = "on_preprint_submission" # kwargs: request, preprint # raised when a preprint is published in the repo - ON_PREPRINT_PUBLICATION = 'on_preprint_publication' + ON_PREPRINT_PUBLICATION = "on_preprint_publication" # kwargs: request, preprint, email_content # raised when a preprint is published in the repo - ON_PREPRINT_NOTIFICATION = 'on_preprint_notification' + ON_PREPRINT_NOTIFICATION = "on_preprint_notification" # kwargs: request, article, comment # raised when a new comment is submitted for a preprint - ON_PREPRINT_COMMENT = 'on_preprint_comment' + ON_PREPRINT_COMMENT = "on_preprint_comment" # kwargs: request, pending_update, action, reason (optional) # raised when an PreprintVersion is approved or declined - ON_PREPRINT_VERSION_UPDATE = 'on_preprint_version_update' + ON_PREPRINT_VERSION_UPDATE = "on_preprint_version_update" # kwargs: request, preprint, review, message # raised when an Review invite is sent - ON_PREPRINT_REVIEW_NOTIFICATION = 'on_preprint_review_notification' + ON_PREPRINT_REVIEW_NOTIFICATION = "on_preprint_review_notification" # kwargs: request, review, status_change [accept, delcine, withdraw, complete] # raised when a Review changes status - ON_PREPRINT_REVIEW_STATUS_CHANGE = 'on_preprint_review_status_change' + ON_PREPRINT_REVIEW_STATUS_CHANGE = "on_preprint_review_status_change" # kwargs: handshake_url, request, article, switch_stage (optional) # raised when a workflow element completes to hand over to the next one - ON_WORKFLOW_ELEMENT_COMPLETE = 'on_workflow_element_complete' + ON_WORKFLOW_ELEMENT_COMPLETE = "on_workflow_element_complete" # kwargs: request, article, article_access # raised when a view or download passes COUNTER-style compliance checks - ON_ARTICLE_ACCESS = 'on_article_access' + ON_ARTICLE_ACCESS = "on_article_access" # kwargs: request, access_request # raised when a user requests access to submit an article to a journal or repo. - ON_ACCESS_REQUEST = 'on_access_request' + ON_ACCESS_REQUEST = "on_access_request" # kwargs: request # raised when a user access request is evaluated by staff. - ON_ACCESS_REQUEST_COMPLETE = 'on_access_request_complete' + ON_ACCESS_REQUEST_COMPLETE = "on_access_request_complete" DEPRECATED_EVENTS = { ON_AUTHOR_PUBLICATION, @@ -299,16 +300,19 @@ def raise_event(event_name, task_object=None, **kwargs): :return: None """ if event_name in Events.DEPRECATED_EVENTS and settings.DEBUG: - raise DeprecationWarning(f'{ event_name } is deprecated.') + raise DeprecationWarning(f"{ event_name } is deprecated.") if settings.DEBUG: - print('Firing event {}'.format(event_name)) + print("Firing event {}".format(event_name)) # destroy/complete tasks that have registered for this event - if event_name != "destroy_tasks" and task_object is not None and isinstance(task_object, - submission_models.Article): - kwargs['event'] = event_name - kwargs['task_obj'] = task_object - Events.raise_event('destroy_tasks', **kwargs) + if ( + event_name != "destroy_tasks" + and task_object is not None + and isinstance(task_object, submission_models.Article) + ): + kwargs["event"] = event_name + kwargs["task_obj"] = task_object + Events.raise_event("destroy_tasks", **kwargs) # fire hooked functions if event_name not in Events._hooks: diff --git a/src/events/registration.py b/src/events/registration.py index ea9955351f..c1a151d0ad 100755 --- a/src/events/registration.py +++ b/src/events/registration.py @@ -14,135 +14,224 @@ from events import logic as event_logic # We always import this as event_logic # Submission -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_SUBMITTED, - transactional_emails.send_submission_acknowledgement, - workflow.workflow_auto_assign_editors) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_ASSIGNED_ACKNOWLEDGE, - transactional_emails.send_editor_assigned_acknowledgements) -event_logic.Events.register_for_event(event_logic.Events.ON_EDITOR_MANUALLY_ASSIGNED, - transactional_emails.send_editor_manually_assigned) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_UNASSIGNED, - transactional_emails.send_editor_unassigned_notice) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_SUBMITTED, + transactional_emails.send_submission_acknowledgement, + workflow.workflow_auto_assign_editors, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_ASSIGNED_ACKNOWLEDGE, + transactional_emails.send_editor_assigned_acknowledgements, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_EDITOR_MANUALLY_ASSIGNED, + transactional_emails.send_editor_manually_assigned, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_UNASSIGNED, + transactional_emails.send_editor_unassigned_notice, +) # Review -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_REQUESTED_NOTIFICATION, - transactional_emails.send_reviewer_requested) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_REQUESTED_ACKNOWLEDGE, - transactional_emails.send_reviewer_requested_acknowledgements) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEW_WITHDRAWL, - transactional_emails.send_reviewer_withdrawl_notice) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_ACCEPTED, - transactional_emails.send_reviewer_accepted_or_decline_acknowledgements) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_DECLINED, - transactional_emails.send_reviewer_accepted_or_decline_acknowledgements) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEW_COMPLETE, - transactional_emails.send_review_complete_acknowledgements) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_DECLINED, - transactional_emails.send_article_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_UNDECLINED, - transactional_emails.send_article_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_ACCEPTED, - transactional_emails.send_article_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_DRAFT_DECISION, - transactional_emails.send_draft_decison) -event_logic.Events.register_for_event(event_logic.Events.ON_DRAFT_DECISION_DECLINED, - transactional_emails.send_draft_decision_declined) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEW_SECURITY_OVERRIDE, - transactional_emails.review_sec_override_notification) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_REQUESTED_NOTIFICATION, + transactional_emails.send_reviewer_requested, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_REQUESTED_ACKNOWLEDGE, + transactional_emails.send_reviewer_requested_acknowledgements, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEW_WITHDRAWL, + transactional_emails.send_reviewer_withdrawl_notice, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_ACCEPTED, + transactional_emails.send_reviewer_accepted_or_decline_acknowledgements, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_DECLINED, + transactional_emails.send_reviewer_accepted_or_decline_acknowledgements, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEW_COMPLETE, + transactional_emails.send_review_complete_acknowledgements, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_DECLINED, transactional_emails.send_article_decision +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_UNDECLINED, transactional_emails.send_article_decision +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_ACCEPTED, transactional_emails.send_article_decision +) +event_logic.Events.register_for_event( + event_logic.Events.ON_DRAFT_DECISION, transactional_emails.send_draft_decison +) +event_logic.Events.register_for_event( + event_logic.Events.ON_DRAFT_DECISION_DECLINED, + transactional_emails.send_draft_decision_declined, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEW_SECURITY_OVERRIDE, + transactional_emails.review_sec_override_notification, +) # Revisions -event_logic.Events.register_for_event(event_logic.Events.ON_REVISIONS_REQUESTED_NOTIFY, - transactional_emails.send_revisions_request) -event_logic.Events.register_for_event(event_logic.Events.ON_REVISIONS_COMPLETE, - transactional_emails.send_revisions_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_REVISIONS_COMPLETE, - transactional_emails.send_revisions_author_receipt) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVISIONS_REQUESTED_NOTIFY, + transactional_emails.send_revisions_request, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVISIONS_COMPLETE, + transactional_emails.send_revisions_complete, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVISIONS_COMPLETE, + transactional_emails.send_revisions_author_receipt, +) # Copyediting -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_ASSIGNMENT, - transactional_emails.send_copyedit_assignment) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_UPDATED, - transactional_emails.send_copyedit_updated) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_DELETED, - transactional_emails.send_copyedit_deleted) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDITOR_DECISION, - transactional_emails.send_copyedit_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW, - transactional_emails.send_copyedit_author_review) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE, - transactional_emails.send_author_copyedit_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_DELETED, - transactional_emails.send_author_copyedit_deleted) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_ASSIGNMENT_COMPLETE, - transactional_emails.send_copyedit_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_REOPEN, - transactional_emails.send_copyedit_reopen) -event_logic.Events.register_for_event(event_logic.Events.ON_COPYEDIT_ACKNOWLEDGE, - transactional_emails.send_copyedit_ack) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_ASSIGNMENT, + transactional_emails.send_copyedit_assignment, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_UPDATED, transactional_emails.send_copyedit_updated +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_DELETED, transactional_emails.send_copyedit_deleted +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDITOR_DECISION, + transactional_emails.send_copyedit_decision, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW, + transactional_emails.send_copyedit_author_review, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_COMPLETE, + transactional_emails.send_author_copyedit_complete, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_AUTHOR_REVIEW_DELETED, + transactional_emails.send_author_copyedit_deleted, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_ASSIGNMENT_COMPLETE, + transactional_emails.send_copyedit_complete, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_REOPEN, transactional_emails.send_copyedit_reopen +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COPYEDIT_ACKNOWLEDGE, transactional_emails.send_copyedit_ack +) # Production -event_logic.Events.register_for_event(event_logic.Events.ON_TYPESET_TASK_ASSIGNED, - transactional_emails.send_typeset_assignment) -event_logic.Events.register_for_event(event_logic.Events.ON_TYPESETTER_DECISION, - transactional_emails.send_typeset_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_TYPESET_TASK_DELETED, - transactional_emails.send_typeset_task_deleted) -event_logic.Events.register_for_event(event_logic.Events.ON_TYPESET_COMPLETE, - transactional_emails.send_typeset_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_PRODUCTION_COMPLETE, - transactional_emails.send_production_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFING_TYPESET_CHANGES_REQUEST, - transactional_emails.send_proofing_typeset_request) -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFING_TYPESET_DECISION, - transactional_emails.send_proofing_typeset_decision) +event_logic.Events.register_for_event( + event_logic.Events.ON_TYPESET_TASK_ASSIGNED, + transactional_emails.send_typeset_assignment, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_TYPESETTER_DECISION, + transactional_emails.send_typeset_decision, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_TYPESET_TASK_DELETED, + transactional_emails.send_typeset_task_deleted, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_TYPESET_COMPLETE, transactional_emails.send_typeset_complete +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PRODUCTION_COMPLETE, + transactional_emails.send_production_complete, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFING_TYPESET_CHANGES_REQUEST, + transactional_emails.send_proofing_typeset_request, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFING_TYPESET_DECISION, + transactional_emails.send_proofing_typeset_decision, +) # Proofing -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFING_MANAGER_ASSIGNMENT, - transactional_emails.fire_proofing_manager_assignment) -event_logic.Events.register_for_event(event_logic.Events.ON_CANCEL_PROOFING_TASK, - transactional_emails.cancel_proofing_task) -event_logic.Events.register_for_event(event_logic.Events.ON_EDIT_PROOFING_TASK, - transactional_emails.edit_proofing_task) -event_logic.Events.register_for_event(event_logic.Events.ON_NOTIFY_PROOFREADER, - transactional_emails.notify_proofreader) -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFREADER_TASK_DECISION, - transactional_emails.send_proofreader_decision) -event_logic.Events.register_for_event(event_logic.Events.ON_COMPLETE_PROOFING_TASK, - transactional_emails.send_proofreader_complete_notification) -event_logic.Events.register_for_event(event_logic.Events.ON_CORRECTIONS_COMPLETE, - transactional_emails.send_corrections_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFING_ACK, - transactional_emails.send_proofing_ack) -event_logic.Events.register_for_event(event_logic.Events.ON_PROOFING_COMPLETE, - transactional_emails.send_proofing_complete) -event_logic.Events.register_for_event(event_logic.Events.ON_CORRECTIONS_CANCELLED, - transactional_emails.send_cancel_corrections) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFING_MANAGER_ASSIGNMENT, + transactional_emails.fire_proofing_manager_assignment, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_CANCEL_PROOFING_TASK, + transactional_emails.cancel_proofing_task, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_EDIT_PROOFING_TASK, transactional_emails.edit_proofing_task +) +event_logic.Events.register_for_event( + event_logic.Events.ON_NOTIFY_PROOFREADER, transactional_emails.notify_proofreader +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFREADER_TASK_DECISION, + transactional_emails.send_proofreader_decision, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_COMPLETE_PROOFING_TASK, + transactional_emails.send_proofreader_complete_notification, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_CORRECTIONS_COMPLETE, + transactional_emails.send_corrections_complete, +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFING_ACK, transactional_emails.send_proofing_ack +) +event_logic.Events.register_for_event( + event_logic.Events.ON_PROOFING_COMPLETE, transactional_emails.send_proofing_complete +) +event_logic.Events.register_for_event( + event_logic.Events.ON_CORRECTIONS_CANCELLED, + transactional_emails.send_cancel_corrections, +) # Prepublication -event_logic.Events.register_for_event(event_logic.Events.ON_PREPUB_NOTIFICATIONS, - transactional_emails.send_prepub_notifications) +event_logic.Events.register_for_event( + event_logic.Events.ON_PREPUB_NOTIFICATIONS, + transactional_emails.send_prepub_notifications, +) # Publication # Note: ON_AUTHOR_PUBLICATION is deprecated -event_logic.Events.register_for_event(event_logic.Events.ON_AUTHOR_PUBLICATION, - transactional_emails.send_author_publication_notification) +event_logic.Events.register_for_event( + event_logic.Events.ON_AUTHOR_PUBLICATION, + transactional_emails.send_author_publication_notification, +) # Send notifications to registered users. -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_SUBMITTED, - journal_logic.fire_submission_notifications) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_ACCEPTED, - journal_logic.fire_acceptance_notifications) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_SUBMITTED, journal_logic.fire_submission_notifications +) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_ACCEPTED, journal_logic.fire_acceptance_notifications +) # Preprints -event_logic.Events.register_for_event(event_logic.Events.ON_PREPRINT_SUBMISSION, - transactional_emails.preprint_submission) +event_logic.Events.register_for_event( + event_logic.Events.ON_PREPRINT_SUBMISSION, transactional_emails.preprint_submission +) -event_logic.Events.register_for_event(event_logic.Events.ON_PREPRINT_NOTIFICATION, - transactional_emails.preprint_notification) +event_logic.Events.register_for_event( + event_logic.Events.ON_PREPRINT_NOTIFICATION, + transactional_emails.preprint_notification, +) -event_logic.Events.register_for_event(event_logic.Events.ON_PREPRINT_COMMENT, - transactional_emails.preprint_comment) +event_logic.Events.register_for_event( + event_logic.Events.ON_PREPRINT_COMMENT, transactional_emails.preprint_comment +) event_logic.Events.register_for_event( event_logic.Events.ON_PREPRINT_VERSION_UPDATE, transactional_emails.preprint_version_update, @@ -169,28 +258,34 @@ ) # wire up task-creation events -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_SUBMITTED, - workflow_tasks.assign_editors) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_SUBMITTED, workflow_tasks.assign_editors +) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_ASSIGNED, - workflow_tasks.select_reviewers) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_ASSIGNED, workflow_tasks.select_reviewers +) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_REQUESTED, - workflow_tasks.do_review_task) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_REQUESTED, workflow_tasks.do_review_task +) -event_logic.Events.register_for_event(event_logic.Events.ON_REVIEWER_ACCEPTED, - workflow_tasks.perform_review_task) +event_logic.Events.register_for_event( + event_logic.Events.ON_REVIEWER_ACCEPTED, workflow_tasks.perform_review_task +) -event_logic.Events.register_for_event(event_logic.Events.ON_ARTICLE_ACCEPTED, - workflow_tasks.create_copyedit_task) +event_logic.Events.register_for_event( + event_logic.Events.ON_ARTICLE_ACCEPTED, workflow_tasks.create_copyedit_task +) -event_logic.Events.register_for_event(event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, - workflow.workflow_element_complete) +event_logic.Events.register_for_event( + event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, workflow.workflow_element_complete +) # wire up the core task destroyer # N.B. this is critical to the operation of the task framework. It automatically tears down tasks that have registered # for event listeners -event_logic.Events.register_for_event('destroy_tasks', core_models.Task.destroyer) +event_logic.Events.register_for_event("destroy_tasks", core_models.Task.destroyer) event_logic.Events.register_for_event( event_logic.Events.ON_ARTICLE_ASSIGNED_TO_ISSUE, diff --git a/src/identifiers/admin.py b/src/identifiers/admin.py index 970d69cbc4..7a76a1286b 100755 --- a/src/identifiers/admin.py +++ b/src/identifiers/admin.py @@ -10,34 +10,49 @@ class BrokenDOIAdmin(admin.ModelAdmin): - list_display = ('identifier', 'resolves_to', 'expected_to_resolve_to', - 'checked', '_journal') - list_filter = ('identifier__article__journal', 'checked') - raw_id_fields = ('article', 'identifier') - search_fields = ('identifier__identifier', 'identifier__article__pk', - 'identifier__article__title') - date_hierarchy = ('checked') + list_display = ( + "identifier", + "resolves_to", + "expected_to_resolve_to", + "checked", + "_journal", + ) + list_filter = ("identifier__article__journal", "checked") + raw_id_fields = ("article", "identifier") + search_fields = ( + "identifier__identifier", + "identifier__article__pk", + "identifier__article__title", + ) + date_hierarchy = "checked" def _journal(self, obj): - return obj.identifier.article.journal if obj else '' + return obj.identifier.article.journal if obj else "" class IdentifierAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', 'id_type', 'identifier', - '_registration_status', '_article_url', '_article', '_journal') - list_filter = ('article__journal', 'id_type') - list_display_links = ('identifier', ) - search_fields = ('pk', 'id_type', 'identifier', 'article__title') - raw_id_fields = ('article',) + list_display = ( + "pk", + "id_type", + "identifier", + "_registration_status", + "_article_url", + "_article", + "_journal", + ) + list_filter = ("article__journal", "id_type") + list_display_links = ("identifier",) + search_fields = ("pk", "id_type", "identifier", "article__title") + raw_id_fields = ("article",) def _article_url(self, obj): - return obj.article.url if obj else '' + return obj.article.url if obj else "" def _registration_status(self, obj): if obj and obj.crossrefstatus: return obj.crossrefstatus.get_message_display() else: - return '' + return "" inlines = [ admin_utils.IdentifierCrossrefStatusInline, @@ -45,33 +60,47 @@ def _registration_status(self, obj): class CrossrefStatusAdmin(admin.ModelAdmin): - list_display = ('pk', 'message', 'identifier', 'latest_deposit', '_journal') - list_filter = ('identifier__article__journal', 'message') - list_display_links = ('message', ) - search_fields = ('pk', 'identifier__identifier', 'message') - raw_id_fields = ('identifier',) - readonly_fields = ('deposits', 'message') + list_display = ("pk", "message", "identifier", "latest_deposit", "_journal") + list_filter = ("identifier__article__journal", "message") + list_display_links = ("message",) + search_fields = ("pk", "identifier__identifier", "message") + raw_id_fields = ("identifier",) + readonly_fields = ("deposits", "message") def _journal(self, obj): - return obj.identifier.article.journal if obj else '' + return obj.identifier.article.journal if obj else "" class CrossrefDepositAdmin(admin.ModelAdmin): - list_display = ('pk', 'file_name', 'has_result', 'queued', - 'success', 'citation_success', - 'date_time', 'polling_attempts', '_journal') - list_filter = ('crossrefstatus__identifier__article__journal', - 'has_result', 'queued', 'success', 'citation_success', - 'date_time', 'polling_attempts') - search_fields = ('pk', 'file_name', 'document', 'result_text') - date_hierarchy = ('date_time') + list_display = ( + "pk", + "file_name", + "has_result", + "queued", + "success", + "citation_success", + "date_time", + "polling_attempts", + "_journal", + ) + list_filter = ( + "crossrefstatus__identifier__article__journal", + "has_result", + "queued", + "success", + "citation_success", + "date_time", + "polling_attempts", + ) + search_fields = ("pk", "file_name", "document", "result_text") + date_hierarchy = "date_time" list_select_related = True def _journal(self, obj): if obj and obj.crossrefstatus_set.first(): return obj.crossrefstatus_set.first().identifier.article.journal else: - return '' + return "" inlines = [ admin_utils.DepositCrossrefStatusInline, diff --git a/src/identifiers/apps.py b/src/identifiers/apps.py index 4371e0fc24..1b47edeb49 100755 --- a/src/identifiers/apps.py +++ b/src/identifiers/apps.py @@ -7,4 +7,4 @@ class IdentifiersConfig(AppConfig): - name = 'identifiers' + name = "identifiers" diff --git a/src/identifiers/forms.py b/src/identifiers/forms.py index e896dd5c9f..98e838ed1a 100644 --- a/src/identifiers/forms.py +++ b/src/identifiers/forms.py @@ -6,38 +6,37 @@ class IdentifierForm(forms.ModelForm): - class Meta: model = models.Identifier fields = ( - 'id_type', - 'identifier', - 'enabled', + "id_type", + "identifier", + "enabled", ) def __init__(self, *args, **kwargs): - self.article = kwargs.pop('article') + self.article = kwargs.pop("article") super(IdentifierForm, self).__init__(*args, **kwargs) def clean(self): super().clean() cleaned_data = self.cleaned_data - + # Test identifier against Regex - id_type = self.cleaned_data.get('id_type') - identifier = self.cleaned_data.get('identifier') + id_type = self.cleaned_data.get("id_type") + identifier = self.cleaned_data.get("identifier") - if id_type == 'doi': + if id_type == "doi": pattern = models.DOI_RE else: pattern = models.PUB_ID_RE if not pattern.match(identifier): self.add_error( - 'identifier', - 'Invalid identifier format.', + "identifier", + "Invalid identifier format.", ) - + # Check identifier doesn't exist elsewhere idents = models.Identifier.objects.filter( id_type=id_type, @@ -47,18 +46,18 @@ def clean(self): if self.instance: idents = idents.exclude(id=self.instance.id) - if id_type == 'doi' and idents.exists(): + if id_type == "doi" and idents.exists(): self.add_error( - 'identifier', - 'This DOI already exists for another Article.', + "identifier", + "This DOI already exists for another Article.", ) else: if idents.filter( article__journal=self.article.journal, ).exists(): self.add_error( - 'identifier', - 'This identifier already exists on: {}.'.format( + "identifier", + "This identifier already exists on: {}.".format( " ".join([ident.article.title for ident in idents]) ), ) diff --git a/src/identifiers/logic.py b/src/identifiers/logic.py index b2a7a65bdc..c9da78fb6b 100755 --- a/src/identifiers/logic.py +++ b/src/identifiers/logic.py @@ -33,6 +33,7 @@ CROSSREF_TIMEOUT_SECONDS = 30 + def register_crossref_doi(identifier): return register_batch_of_crossref_dois([identifier.article]) @@ -40,7 +41,7 @@ def register_crossref_doi(identifier): def register_batch_of_crossref_dois(articles, **kwargs): journals = set([article.journal for article in articles]) if len(journals) > 1: - status = 'Articles must all be from the same journal' + status = "Articles must all be from the same journal" error = True logger.debug(status) return status, error @@ -50,18 +51,22 @@ def register_batch_of_crossref_dois(articles, **kwargs): use_crossref, test_mode, missing_settings = check_crossref_settings(journal) if use_crossref and not missing_settings: - mode = 'test' if test_mode else 'live' - desc = f'DOI registration running in f{mode} mode' - util_models.LogEntry.bulk_add_simple_entry('Submission', desc, 'Info', targets=articles) + mode = "test" if test_mode else "live" + desc = f"DOI registration running in f{mode} mode" + util_models.LogEntry.bulk_add_simple_entry( + "Submission", desc, "Info", targets=articles + ) identifiers = get_dois_for_articles(articles, create=True) return send_crossref_deposit(test_mode, identifiers, journal) elif not use_crossref: - status = f'Crossref Disabled ({journal.code})' + status = f"Crossref Disabled ({journal.code})" error = True logger.debug(status) return status, error elif use_crossref and missing_settings: - status = f'Missing Crossref settings ({journal.code}): '+', '.join(missing_settings) + status = f"Missing Crossref settings ({journal.code}): " + ", ".join( + missing_settings + ) error = True logger.debug(status) return status, error @@ -70,53 +75,56 @@ def register_batch_of_crossref_dois(articles, **kwargs): @cache(30) def check_crossref_settings(journal): use_crossref = setting_handler.get_setting( - 'Identifiers', - 'use_crossref', - journal + "Identifiers", "use_crossref", journal ).processed_value if not use_crossref: - logger.info("[DOI] Not using Crossref DOIs on this journal. " \ - "Aborting registration.") + logger.info( + "[DOI] Not using Crossref DOIs on this journal. " "Aborting registration." + ) - test_mode = setting_handler.get_setting( - 'Identifiers', - 'crossref_test', - journal - ).processed_value or settings.DEBUG + test_mode = ( + setting_handler.get_setting( + "Identifiers", "crossref_test", journal + ).processed_value + or settings.DEBUG + ) settings_to_check = [ - 'crossref_prefix', - 'crossref_username', - 'crossref_password', - 'crossref_name', - 'crossref_email', - 'crossref_registrant' + "crossref_prefix", + "crossref_username", + "crossref_password", + "crossref_name", + "crossref_email", + "crossref_registrant", ] missing_settings = [] for setting_name in settings_to_check: setting_value = setting_handler.get_setting( - 'Identifiers', - setting_name, - journal + "Identifiers", setting_name, journal ).processed_value if not setting_value: missing_settings.append(setting_name) if not journal.code: - missing_settings.append('journal__code') + missing_settings.append("journal__code") return use_crossref, test_mode, missing_settings @cache(30) def get_poll_settings(journal): - test_mode = setting_handler.get_setting('Identifiers', - 'crossref_test', - journal).processed_value or settings.DEBUG - username = setting_handler.get_setting('Identifiers', 'crossref_username', - journal).processed_value - password = setting_handler.get_setting('Identifiers', 'crossref_password', - journal).processed_value + test_mode = ( + setting_handler.get_setting( + "Identifiers", "crossref_test", journal + ).processed_value + or settings.DEBUG + ) + username = setting_handler.get_setting( + "Identifiers", "crossref_username", journal + ).processed_value + password = setting_handler.get_setting( + "Identifiers", "crossref_password", journal + ).processed_value return test_mode, username, password @@ -124,28 +132,27 @@ def get_dois_for_articles(articles, create=False): identifiers = [] for article in articles: try: - identifier = article.get_identifier('doi', object=True) + identifier = article.get_identifier("doi", object=True) if not identifier and create: identifier = generate_crossref_doi_with_pattern(article) if identifier: identifiers.append(identifier) except AttributeError as e: - logger.debug(f'Error with article {article.pk}: {e}') + logger.debug(f"Error with article {article.pk}: {e}") return identifiers def poll_dois_for_articles(articles, **kwargs): clear_cache() - start = kwargs.pop('start', time.time()) - timeout = kwargs.pop('timeout', CROSSREF_TIMEOUT_SECONDS) + start = kwargs.pop("start", time.time()) + timeout = kwargs.pop("timeout", CROSSREF_TIMEOUT_SECONDS) - status = '' + status = "" error = False identifiers = get_dois_for_articles(articles) polled = set() for i, identifier in enumerate(identifiers): - # Time out gracefully if timeout and time.time() > start + timeout: error = True @@ -162,7 +169,7 @@ def poll_dois_for_articles(articles, **kwargs): status, error = deposit.poll() polled.add(deposit) if len(polled) and len(polled) % 20 == 0: - time.sleep(.15) + time.sleep(0.15) except: continue @@ -176,73 +183,117 @@ def poll_dois_for_articles(articles, **kwargs): return status, error + def register_crossref_component(article, xml, supp_file): - use_crossref = setting_handler.get_setting('Identifiers', 'use_crossref', - article.journal).processed_value + use_crossref = setting_handler.get_setting( + "Identifiers", "use_crossref", article.journal + ).processed_value if not use_crossref: - logger.info("[DOI] Not using Crossref DOIs on this journal. Aborting registration.") + logger.info( + "[DOI] Not using Crossref DOIs on this journal. Aborting registration." + ) return - test_mode = setting_handler.get_setting( - 'Identifiers', 'crossref_test', article.journal - ).processed_value or settings.DEBUG + test_mode = ( + setting_handler.get_setting( + "Identifiers", "crossref_test", article.journal + ).processed_value + or settings.DEBUG + ) if test_mode: - util_models.LogEntry.add_entry('Submission', "DOI component registration running in test mode", 'Info', - target=article) + util_models.LogEntry.add_entry( + "Submission", + "DOI component registration running in test mode", + "Info", + target=article, + ) else: - util_models.LogEntry.add_entry('Submission', "DOI component registration running in live mode", 'Info', - target=article) + util_models.LogEntry.add_entry( + "Submission", + "DOI component registration running in live mode", + "Info", + target=article, + ) - doi_prefix = setting_handler.get_setting('Identifiers', 'crossref_prefix', article.journal) - username = setting_handler.get_setting('Identifiers', 'crossref_username', article.journal).processed_value - password = setting_handler.get_setting('Identifiers', 'crossref_password', article.journal).processed_value + doi_prefix = setting_handler.get_setting( + "Identifiers", "crossref_prefix", article.journal + ) + username = setting_handler.get_setting( + "Identifiers", "crossref_username", article.journal + ).processed_value + password = setting_handler.get_setting( + "Identifiers", "crossref_password", article.journal + ).processed_value - depositor = Depositor(prefix=doi_prefix, api_user=username, api_key=password, use_test_server=test_mode) - response = depositor.register_doi(submission_id='component{0}.xml'.format(uuid4()), request_xml=xml) + depositor = Depositor( + prefix=doi_prefix, + api_user=username, + api_key=password, + use_test_server=test_mode, + ) + response = depositor.register_doi( + submission_id="component{0}.xml".format(uuid4()), request_xml=xml + ) logger.debug("[CROSSREF:DEPOSIT:{0}] Sending".format(article.id)) - logger.debug("[CROSSREF:DEPOSIT:%s] Response code %s" % (article.id, response.status_code)) + logger.debug( + "[CROSSREF:DEPOSIT:%s] Response code %s" % (article.id, response.status_code) + ) if response.status_code != 200: - util_models.LogEntry.add_entry('Error', - "Error depositing: {0}. {1}".format(response.status_code, response.text), - 'Debug', - target=article) + util_models.LogEntry.add_entry( + "Error", + "Error depositing: {0}. {1}".format(response.status_code, response.text), + "Debug", + target=article, + ) status = "Error depositing: {code}, {text}".format( - code=response.status_code, - text=response.text + code=response.status_code, text=response.text ) logger.error(status) logger.error(response.text) error = True else: - util_models.LogEntry.add_entry('Submission', "Deposited DOI.", 'Info', target=article) + util_models.LogEntry.add_entry( + "Submission", "Deposited DOI.", "Info", target=article + ) def create_crossref_doi_batch_context(journal, identifiers): timestamp_suffix = journal.get_setting( - 'crossref', - 'crossref_date_suffix', + "crossref", + "crossref_date_suffix", ) template_context = { - 'batch_id': uuid4(), - 'now': datetime.datetime.now(), - 'timestamp': int(round((datetime.datetime.now() - datetime.datetime(1970, 1, 1)).total_seconds())), - 'timestamp_suffix': timestamp_suffix, - 'depositor_name': setting_handler.get_setting('Identifiers', 'crossref_name', - journal).processed_value, - 'depositor_email': setting_handler.get_setting('Identifiers', 'crossref_email', - journal).processed_value, - 'registrant': setting_handler.get_setting('Identifiers', 'crossref_registrant', - journal).processed_value, - 'is_conference': journal.is_conference or False, + "batch_id": uuid4(), + "now": datetime.datetime.now(), + "timestamp": int( + round( + ( + datetime.datetime.now() - datetime.datetime(1970, 1, 1) + ).total_seconds() + ) + ), + "timestamp_suffix": timestamp_suffix, + "depositor_name": setting_handler.get_setting( + "Identifiers", "crossref_name", journal + ).processed_value, + "depositor_email": setting_handler.get_setting( + "Identifiers", "crossref_email", journal + ).processed_value, + "registrant": setting_handler.get_setting( + "Identifiers", "crossref_registrant", journal + ).processed_value, + "is_conference": journal.is_conference or False, } - template_context['crossref_issues'] = create_crossref_issues_context(journal, identifiers) + template_context["crossref_issues"] = create_crossref_issues_context( + journal, identifiers + ) return template_context @@ -290,34 +341,32 @@ def create_crossref_issue_context( publication_title=None, ): crossref_issue = {} - crossref_issue['journal'] = create_crossref_journal_context( + crossref_issue["journal"] = create_crossref_journal_context( journal, ISSN_override, publication_title, ) - crossref_issue['issue'] = issue + crossref_issue["issue"] = issue - crossref_issue['articles'] = [] + crossref_issue["articles"] = [] for identifier in identifiers: article = identifier.article if article.issue == issue: article_context = create_crossref_article_context(article, identifier) - crossref_issue['articles'].append(article_context) + crossref_issue["articles"].append(article_context) return crossref_issue @cache(30) def create_crossref_journal_context( - journal, - ISSN_override=None, - publication_title=None + journal, ISSN_override=None, publication_title=None ): journal_data = { - 'title': publication_title or journal.name, - 'journal_issn': ISSN_override or journal.issn, - 'print_issn': journal.print_issn, - 'press': journal.press, + "title": publication_title or journal.name, + "journal_issn": ISSN_override or journal.issn, + "print_issn": journal.print_issn, + "press": journal.press, } if journal.doi: journal_data["doi"] = journal.doi @@ -325,22 +374,26 @@ def create_crossref_journal_context( return journal_data + def create_crossref_article_context(article, identifier=None): template_context = { - 'id': article.pk, - 'title': '{0}{1}{2}'.format( + "id": article.pk, + "title": "{0}{1}{2}".format( article.title, - ' ' if article.subtitle is not None else '', - article.subtitle if article.subtitle is not None else ''), - 'doi': identifier.identifier if identifier else render_doi_from_pattern(article), - 'url': article.url, - 'authors': article.frozenauthor_set.all(), - 'abstract': strip_tags(article.abstract or ''), - 'date_accepted': article.date_accepted, - 'date_published': article.date_published, - 'license': article.license.url if article.license else '', - 'pages': article.page_numbers, - 'scheduled': article.scheduled_for_publication, + " " if article.subtitle is not None else "", + article.subtitle if article.subtitle is not None else "", + ), + "doi": identifier.identifier + if identifier + else render_doi_from_pattern(article), + "url": article.url, + "authors": article.frozenauthor_set.all(), + "abstract": strip_tags(article.abstract or ""), + "date_accepted": article.date_accepted, + "date_published": article.date_published, + "license": article.license.url if article.license else "", + "pages": article.page_numbers, + "scheduled": article.scheduled_for_publication, } # append citations for i4oc compatibility @@ -349,13 +402,13 @@ def create_crossref_article_context(article, identifier=None): # append PDFs for similarity check compatibility pdfs = article.pdfs if len(pdfs) > 0: - template_context['pdf_url'] = article.pdf_url + template_context["pdf_url"] = article.pdf_url return template_context def extract_citations_for_crossref(article): - """ Extracts the citations in a format compatible for crossref deposits + """Extracts the citations in a format compatible for crossref deposits It can only handle articles with an XML galley using a DTD compatible with the XSL provided by crossref themselves @@ -364,15 +417,15 @@ def extract_citations_for_crossref(article): """ render_galley = article.get_render_galley citations = None - if render_galley and render_galley.type == 'xml': + if render_galley and render_galley.type == "xml": try: - logger.debug('Doing crossref citation list transform:') + logger.debug("Doing crossref citation list transform:") xml_transformed = render_galley.render_crossref() logger.debug(xml_transformed) # extract the citation list - souped_xml = BeautifulSoup(str(xml_transformed), 'lxml') - citation_list = souped_xml.find('citation_list') + souped_xml = BeautifulSoup(str(xml_transformed), "lxml") + citation_list = souped_xml.find("citation_list") # Crossref only accepts DOIs on identifier format (not url) url_element = "doi.org/" @@ -382,15 +435,13 @@ def extract_citations_for_crossref(article): if citation_list: citations = str(citation_list.extract()) - citations = citations.replace( - "author{str(idx)}_{prop}: {val}' + if k == "authors": + for idx, author in enumerate(crossref_context["authors"], start=1): + for prop in [ + "first_name", + "middle_name", + "last_name", + "department", + "institution", + "orcid", + ]: + val = getattr(author, prop) or "" + metadata_printout += f"
author{str(idx)}_{prop}: {val}" else: metadata_printout += f'
{k}: {"" if v == None else v}' return metadata_printout else: - return '' + return "" + def get_preprint_tempate_context(request, identifier): article = identifier.article template_context = { - 'batch_id': uuid4(), - 'timestamp': int(round((datetime.datetime.now() - datetime.datetime(1970, 1, 1)).total_seconds())), - 'depositor_name': request.press.name, - 'depositor_email': request.press.main_contact, - 'registrant': request.press.name, - 'journal_title': request.press.name, - 'journal_issn': '', - 'journal_month': identifier.article.date_published.month, - 'journal_day': identifier.article.date_published.day, - 'journal_year': identifier.article.date_published.year, - 'journal_volume': 0, - 'journal_issue': 0, - 'article_title': '{0}{1}{2}'.format( + "batch_id": uuid4(), + "timestamp": int( + round( + ( + datetime.datetime.now() - datetime.datetime(1970, 1, 1) + ).total_seconds() + ) + ), + "depositor_name": request.press.name, + "depositor_email": request.press.main_contact, + "registrant": request.press.name, + "journal_title": request.press.name, + "journal_issn": "", + "journal_month": identifier.article.date_published.month, + "journal_day": identifier.article.date_published.day, + "journal_year": identifier.article.date_published.year, + "journal_volume": 0, + "journal_issue": 0, + "article_title": "{0}{1}{2}".format( identifier.article.title, - ' ' if identifier.article.subtitle is not None else '', - identifier.article.subtitle if identifier.article.subtitle is not None else ''), - 'authors': identifier.article.authors.all(), - 'article_month': identifier.article.date_published.month, - 'article_day': identifier.article.date_published.day, - 'article_year': identifier.article.date_published.year, - 'doi': identifier.identifier, - 'article_url': reverse('preprints_article', kwargs={'article_id': article.pk}), + " " if identifier.article.subtitle is not None else "", + identifier.article.subtitle + if identifier.article.subtitle is not None + else "", + ), + "authors": identifier.article.authors.all(), + "article_month": identifier.article.date_published.month, + "article_day": identifier.article.date_published.day, + "article_year": identifier.article.date_published.year, + "doi": identifier.identifier, + "article_url": reverse("preprints_article", kwargs={"article_id": article.pk}), } return template_context @@ -605,57 +701,76 @@ def register_preprint_doi(request, crossref_enabled, identifier): """ if not crossref_enabled: - messages.add_message(request, messages.WARNING, 'Crossref DOIs are not enabled for this preprint service.') + messages.add_message( + request, + messages.WARNING, + "Crossref DOIs are not enabled for this preprint service.", + ) else: - # Set the URL for depositing based on whether we are in test mode - if request.press.get_setting_value('Crossref Test Mode') == 'On': + if request.press.get_setting_value("Crossref Test Mode") == "On": url = CROSSREF_TEST_URL else: url = CROSSREF_LIVE_URL template_context = get_preprint_tempate_context(request, identifier) - template = 'common/identifiers/crossref.xml' + template = "common/identifiers/crossref.xml" rendered = render_to_string(template, template_context) pdfs = identifier.article.pdfs if len(pdfs) > 0: - template_context['pdf_url'] = identifier.article.pdf_url - - response = requests.post(url, data=rendered.encode('utf-8'), - auth=(request.press.get_setting_value("Crossref Login"), - request.press.get_setting_value("Crossref Password")), - headers={"Content-Type": "application/vnd.crossref.deposit+xml"}) + template_context["pdf_url"] = identifier.article.pdf_url + + response = requests.post( + url, + data=rendered.encode("utf-8"), + auth=( + request.press.get_setting_value("Crossref Login"), + request.press.get_setting_value("Crossref Password"), + ), + headers={"Content-Type": "application/vnd.crossref.deposit+xml"}, + ) if response.status_code != 200: - util_models.LogEntry.add_entry('Error', - "Error depositing: {0}. {1}".format(response.status_code, response.text), - 'Debug', - target=identifier.article) + util_models.LogEntry.add_entry( + "Error", + "Error depositing: {0}. {1}".format( + response.status_code, response.text + ), + "Debug", + target=identifier.article, + ) logger.error("Error depositing: {}".format(response.status_code)) logger.error(response.text) else: - token = response.json()['message']['batch-id'] - status = response.json()['message']['status'] - util_models.LogEntry.add_entry('Submission', "Deposited {0}. Status: {1}".format(token, status), 'Info', - target=identifier.article) - logger.info("Status of {} in {}: {}".format(token, identifier.identifier, status)) + token = response.json()["message"]["batch-id"] + status = response.json()["message"]["status"] + util_models.LogEntry.add_entry( + "Submission", + "Deposited {0}. Status: {1}".format(token, status), + "Info", + target=identifier.article, + ) + logger.info( + "Status of {} in {}: {}".format(token, identifier.identifier, status) + ) def generate_issue_doi_from_logic(issue): doi_prefix = setting_handler.get_setting( - 'Identifiers', 'crossref_prefix', issue.journal).value + "Identifiers", "crossref_prefix", issue.journal + ).value doi_suffix = render_template.get_requestless_content( - {'issue': issue}, - issue.journal, - 'issue_doi_pattern', - group_name='Identifiers') - return '{0}/{1}'.format(doi_prefix, doi_suffix) + {"issue": issue}, issue.journal, "issue_doi_pattern", group_name="Identifiers" + ) + return "{0}/{1}".format(doi_prefix, doi_suffix) def auto_assign_issue_doi(issue): auto_assign_enabled = setting_handler.get_setting( - 'Identifiers', 'register_issue_dois', issue.journal, + "Identifiers", + "register_issue_dois", + issue.journal, default=True, ).processed_value if auto_assign_enabled and not issue.doi: diff --git a/src/identifiers/management/commands/poll_crossref.py b/src/identifiers/management/commands/poll_crossref.py index b4f29a0c28..c0a6414aad 100755 --- a/src/identifiers/management/commands/poll_crossref.py +++ b/src/identifiers/management/commands/poll_crossref.py @@ -9,6 +9,7 @@ MAX_POLLING_ATTEMPTS = 20 + class Command(BaseCommand): """ Executes all of Janeway's cron tasks. @@ -17,22 +18,21 @@ class Command(BaseCommand): help = "Poll 20 Crossref tasks that need doing." def add_arguments(self, parser): - parser.add_argument('tasks', + parser.add_argument( + "tasks", default=20, type=int, - nargs='?', - help="The maximum number of tasks that will be run" + nargs="?", + help="The maximum number of tasks that will be run", ) - parser.add_argument('attempts', + parser.add_argument( + "attempts", default=MAX_POLLING_ATTEMPTS, type=int, - nargs='?', - help="The maximum number of attempts to poll a single deposit" - ) - parser.add_argument('--journal_code', - nargs='?', - help="Filter tasks by journal" + nargs="?", + help="The maximum number of attempts to poll a single deposit", ) + parser.add_argument("--journal_code", nargs="?", help="Filter tasks by journal") def handle(self, *args, **options): """Polls all outstanding deposits. @@ -42,16 +42,19 @@ def handle(self, *args, **options): :return: None """ print("Polling deposits.") - outstanding_deposits = (Q(queued=True) | Q(has_result=False)) - filter_args= [outstanding_deposits] - filter_kwargs={} + outstanding_deposits = Q(queued=True) | Q(has_result=False) + filter_args = [outstanding_deposits] + filter_kwargs = {} if options.get("journal_code"): - filter_kwargs["identifier__article__journal__code"] = options["journal_code"] + filter_kwargs["identifier__article__journal__code"] = options[ + "journal_code" + ] if options.get("attempts") > 0: filter_kwargs["polling_attempts__lte"] = options["attempts"] - deposits = models.CrossrefDeposit.objects.filter( - *filter_args, **filter_kwargs)[:options["tasks"]] + deposits = models.CrossrefDeposit.objects.filter(*filter_args, **filter_kwargs)[ + : options["tasks"] + ] if deposits.count() < 1: print("No deposits to handle") @@ -71,18 +74,17 @@ def handle(self, *args, **options): # Update existing CrossrefStatus objects. # Assumes that if a deposit exists, a status exists # for each identifier in the deposit batch. - for status in models.CrossrefStatus.objects.filter( - deposits=deposit - ): + for status in models.CrossrefStatus.objects.filter(deposits=deposit): status.update() stale_attempts = models.CrossrefDeposit.objects.filter( - polling_attempts__gt=MAX_POLLING_ATTEMPTS, - success=False, + polling_attempts__gt=MAX_POLLING_ATTEMPTS, + success=False, ) if stale_attempts: logger.warning( "Found {0} deposits with more than {1} poll attempts".format( - stale_attempts.count(), MAX_POLLING_ATTEMPTS, + stale_attempts.count(), + MAX_POLLING_ATTEMPTS, ) ) diff --git a/src/identifiers/migrations/0001_initial.py b/src/identifiers/migrations/0001_initial.py index 4796ce0e5b..89ee9914e3 100755 --- a/src/identifiers/migrations/0001_initial.py +++ b/src/identifiers/migrations/0001_initial.py @@ -6,29 +6,53 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='BrokenDOI', + name="BrokenDOI", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('checked', models.DateTimeField()), - ('resolves_to', models.URLField()), - ('expected_to_resolve_to', models.URLField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("checked", models.DateTimeField()), + ("resolves_to", models.URLField()), + ("expected_to_resolve_to", models.URLField()), ], ), migrations.CreateModel( - name='Identifier', + name="Identifier", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('id_type', models.CharField(choices=[('doi', 'DOI'), ('uri', 'URI'), ('pubid', 'Publisher ID')], max_length=300)), - ('identifier', models.CharField(max_length=300)), - ('enabled', models.BooleanField(default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "id_type", + models.CharField( + choices=[ + ("doi", "DOI"), + ("uri", "URI"), + ("pubid", "Publisher ID"), + ], + max_length=300, + ), + ), + ("identifier", models.CharField(max_length=300)), + ("enabled", models.BooleanField(default=True)), ], ), ] diff --git a/src/identifiers/migrations/0002_auto_20170711_1203.py b/src/identifiers/migrations/0002_auto_20170711_1203.py index 2421e5a5b1..cd19c342e8 100755 --- a/src/identifiers/migrations/0002_auto_20170711_1203.py +++ b/src/identifiers/migrations/0002_auto_20170711_1203.py @@ -7,23 +7,26 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('submission', '0001_initial'), - ('identifiers', '0001_initial'), + ("submission", "0001_initial"), + ("identifiers", "0001_initial"), ] operations = [ migrations.AddField( - model_name='identifier', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="identifier", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='brokendoi', - name='identifier', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='identifiers.Identifier'), + model_name="brokendoi", + name="identifier", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="identifiers.Identifier" + ), ), ] diff --git a/src/identifiers/migrations/0003_brokendoi_journal.py b/src/identifiers/migrations/0003_brokendoi_journal.py index a609aae5f0..bb350df402 100755 --- a/src/identifiers/migrations/0003_brokendoi_journal.py +++ b/src/identifiers/migrations/0003_brokendoi_journal.py @@ -7,17 +7,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0004_auto_20170813_1302'), - ('identifiers', '0002_auto_20170711_1203'), + ("journal", "0004_auto_20170813_1302"), + ("identifiers", "0002_auto_20170711_1203"), ] operations = [ migrations.AddField( - model_name='brokendoi', - name='journal', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="brokendoi", + name="journal", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), preserve_default=False, ), ] diff --git a/src/identifiers/migrations/0004_auto_20170921_1113.py b/src/identifiers/migrations/0004_auto_20170921_1113.py index f2d8202737..222af0e7dd 100755 --- a/src/identifiers/migrations/0004_auto_20170921_1113.py +++ b/src/identifiers/migrations/0004_auto_20170921_1113.py @@ -7,21 +7,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0011_auto_20170921_0937'), - ('identifiers', '0003_brokendoi_journal'), + ("submission", "0011_auto_20170921_0937"), + ("identifiers", "0003_brokendoi_journal"), ] operations = [ migrations.RemoveField( - model_name='brokendoi', - name='journal', + model_name="brokendoi", + name="journal", ), migrations.AddField( - model_name='brokendoi', - name='article', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="brokendoi", + name="article", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), preserve_default=False, ), ] diff --git a/src/identifiers/migrations/0005_changes_identifier_choices.py b/src/identifiers/migrations/0005_changes_identifier_choices.py index 9ca1fdc630..13e46357bf 100644 --- a/src/identifiers/migrations/0005_changes_identifier_choices.py +++ b/src/identifiers/migrations/0005_changes_identifier_choices.py @@ -6,15 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('identifiers', '0004_auto_20170921_1113'), + ("identifiers", "0004_auto_20170921_1113"), ] operations = [ migrations.AlterField( - model_name='identifier', - name='id_type', - field=models.CharField(choices=[('doi', 'DOI'), ('uri', 'URI Path'), ('pubid', 'Publisher ID')], max_length=300), + model_name="identifier", + name="id_type", + field=models.CharField( + choices=[ + ("doi", "DOI"), + ("uri", "URI Path"), + ("pubid", "Publisher ID"), + ], + max_length=300, + ), ), ] diff --git a/src/identifiers/migrations/0006_crossrefdeposit.py b/src/identifiers/migrations/0006_crossrefdeposit.py index 33c4f5e207..384e908c91 100644 --- a/src/identifiers/migrations/0006_crossrefdeposit.py +++ b/src/identifiers/migrations/0006_crossrefdeposit.py @@ -8,25 +8,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('identifiers', '0005_changes_identifier_choices'), + ("identifiers", "0005_changes_identifier_choices"), ] operations = [ migrations.CreateModel( - name='CrossrefDeposit', + name="CrossrefDeposit", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('has_result', models.BooleanField(default=False)), - ('success', models.BooleanField(default=False)), - ('queued', models.BooleanField(default=False)), - ('citation_success', models.BooleanField(default=False)), - ('result_text', models.TextField(blank=True, null=True)), - ('file_name', models.CharField(max_length=255)), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('polling_attempts', models.PositiveIntegerField(default=0)), - ('identifier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='identifiers.Identifier')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("has_result", models.BooleanField(default=False)), + ("success", models.BooleanField(default=False)), + ("queued", models.BooleanField(default=False)), + ("citation_success", models.BooleanField(default=False)), + ("result_text", models.TextField(blank=True, null=True)), + ("file_name", models.CharField(max_length=255)), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ("polling_attempts", models.PositiveIntegerField(default=0)), + ( + "identifier", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="identifiers.Identifier", + ), + ), ], ), ] diff --git a/src/identifiers/migrations/0007_batch_doi_registration_20220315.py b/src/identifiers/migrations/0007_batch_doi_registration_20220315.py index feafa111ba..7d1979ec25 100644 --- a/src/identifiers/migrations/0007_batch_doi_registration_20220315.py +++ b/src/identifiers/migrations/0007_batch_doi_registration_20220315.py @@ -8,49 +8,91 @@ class Migration(migrations.Migration): dependencies = [ - ('identifiers', '0006_crossrefdeposit'), + ("identifiers", "0006_crossrefdeposit"), ] operations = [ migrations.AddField( - model_name='crossrefdeposit', - name='document', - field=models.TextField(blank=True, help_text='The deposit document with rendered XML for the DOI batch.', null=True), + model_name="crossrefdeposit", + name="document", + field=models.TextField( + blank=True, + help_text="The deposit document with rendered XML for the DOI batch.", + null=True, + ), ), migrations.AlterField( - model_name='crossrefdeposit', - name='identifier', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='identifiers.Identifier'), + model_name="crossrefdeposit", + name="identifier", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="identifiers.Identifier", + ), ), migrations.AlterField( - model_name='crossrefdeposit', - name='result_text', - field=models.TextField(blank=True, default=''), + model_name="crossrefdeposit", + name="result_text", + field=models.TextField(blank=True, default=""), preserve_default=False, ), migrations.AlterModelOptions( - name='crossrefdeposit', - options={'ordering': ('-date_time',)}, + name="crossrefdeposit", + options={"ordering": ("-date_time",)}, ), migrations.CreateModel( - name='CrossrefStatus', + name="CrossrefStatus", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('message', models.CharField(blank=True, choices=[('untried', 'Not yet registered'), ('queued', 'Queued at Crossref'), ('registered', 'Registered'), ('registered_but_citation_problems', 'Registered (but some citations not correctly parsed)'), ('warning', 'Registered with warning'), ('failed', 'Registration failed'), ('', 'Unknown')], default='Unknown', help_text='A user-friendly message about the status of registration with Crossref.', max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "message", + models.CharField( + blank=True, + choices=[ + ("untried", "Not yet registered"), + ("queued", "Queued at Crossref"), + ("registered", "Registered"), + ( + "registered_but_citation_problems", + "Registered (but some citations not correctly parsed)", + ), + ("warning", "Registered with warning"), + ("failed", "Registration failed"), + ("", "Unknown"), + ], + default="Unknown", + help_text="A user-friendly message about the status of registration with Crossref.", + max_length=255, + ), + ), ], ), migrations.AddField( - model_name='crossrefstatus', - name='deposits', - field=models.ManyToManyField(blank=True, to='identifiers.CrossrefDeposit'), + model_name="crossrefstatus", + name="deposits", + field=models.ManyToManyField(blank=True, to="identifiers.CrossrefDeposit"), ), migrations.AddField( - model_name='crossrefstatus', - name='identifier', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='identifiers.Identifier'), + model_name="crossrefstatus", + name="identifier", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="identifiers.Identifier" + ), ), migrations.AlterModelOptions( - name='crossrefstatus', - options={'verbose_name': 'Crossref status', 'verbose_name_plural': 'Crossref statuses'}, + name="crossrefstatus", + options={ + "verbose_name": "Crossref status", + "verbose_name_plural": "Crossref statuses", + }, ), ] diff --git a/src/identifiers/migrations/0008_batch_doi_registration_continued_20220524.py b/src/identifiers/migrations/0008_batch_doi_registration_continued_20220524.py index 944753a1f6..2f29ad9faa 100644 --- a/src/identifiers/migrations/0008_batch_doi_registration_continued_20220524.py +++ b/src/identifiers/migrations/0008_batch_doi_registration_continued_20220524.py @@ -5,9 +5,10 @@ from django.db import migrations, models import django.db.models.deletion + def migrate_current_crossref_deposit_identifiers(apps, schema_editor): CrossrefDeposit = apps.get_model("identifiers", "CrossrefDeposit") - for crossref_deposit in CrossrefDeposit.objects.all().order_by('date_time'): + for crossref_deposit in CrossrefDeposit.objects.all().order_by("date_time"): if crossref_deposit.identifier: CrossrefStatus = apps.get_model("identifiers", "CrossrefStatus") crossref_status, created = CrossrefStatus.objects.get_or_create( @@ -18,16 +19,16 @@ def migrate_current_crossref_deposit_identifiers(apps, schema_editor): # print('CrossrefStatus attached to doi:', crossref_status.identifier.identifier) # print('CrossrefStatus message before ifs:', crossref_status.message) if crossref_deposit.queued: - crossref_status.message = 'queued' + crossref_status.message = "queued" elif crossref_deposit.success: if not crossref_deposit.citation_success: - crossref_status.message = 'registered_but_citation_problems' + crossref_status.message = "registered_but_citation_problems" else: - crossref_status.message = 'registered' + crossref_status.message = "registered" elif crossref_deposit.has_result: - crossref_status.message = 'failed' + crossref_status.message = "failed" else: - crossref_status.message = '' + crossref_status.message = "" crossref_status.deposits.add(crossref_deposit) crossref_status.save() @@ -38,11 +39,15 @@ def migrate_current_crossref_deposit_identifiers(apps, schema_editor): # print('CrossrefDeposit with identifier removed:', crossref_deposit.identifier) # print('CrossrefDeposit result text:', crossref_deposit.result_text) + class Migration(migrations.Migration): dependencies = [ - ('identifiers', '0007_batch_doi_registration_20220315'), + ("identifiers", "0007_batch_doi_registration_20220315"), ] operations = [ - migrations.RunPython(migrate_current_crossref_deposit_identifiers, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + migrate_current_crossref_deposit_identifiers, + reverse_code=migrations.RunPython.noop, + ), ] diff --git a/src/identifiers/migrations/0009_deduplicate_identifiers_20220527.py b/src/identifiers/migrations/0009_deduplicate_identifiers_20220527.py index 429558bf07..a125478da0 100644 --- a/src/identifiers/migrations/0009_deduplicate_identifiers_20220527.py +++ b/src/identifiers/migrations/0009_deduplicate_identifiers_20220527.py @@ -12,11 +12,19 @@ def deduplicate_identifiers(apps, schema_editor): articles = Article.objects.all() for article in articles: identifiers_for_article = Identifier.objects.filter(article=article) - for id_type in ['doi', 'uri', 'pubid']: + for id_type in ["doi", "uri", "pubid"]: identifiers_of_type = identifiers_for_article.filter(id_type=id_type) - for doi_string in set(identifiers_of_type.values_list('identifier', flat=True)): - to_keep = identifiers_of_type.filter(identifier=doi_string).order_by('-pk').first() - duplicates = identifiers_of_type.filter(identifier=doi_string).exclude(pk=to_keep.pk) + for doi_string in set( + identifiers_of_type.values_list("identifier", flat=True) + ): + to_keep = ( + identifiers_of_type.filter(identifier=doi_string) + .order_by("-pk") + .first() + ) + duplicates = identifiers_of_type.filter(identifier=doi_string).exclude( + pk=to_keep.pk + ) if duplicates: # print('\n\n\n') # print('To keep:') @@ -31,9 +39,11 @@ def deduplicate_identifiers(apps, schema_editor): class Migration(migrations.Migration): atomic = False dependencies = [ - ('identifiers', '0008_batch_doi_registration_continued_20220524'), + ("identifiers", "0008_batch_doi_registration_continued_20220524"), ] operations = [ - migrations.RunPython(deduplicate_identifiers, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + deduplicate_identifiers, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/identifiers/models.py b/src/identifiers/models.py index d9fcbcad1c..636b5f7ab9 100755 --- a/src/identifiers/models.py +++ b/src/identifiers/models.py @@ -26,23 +26,18 @@ logger = get_logger(__name__) identifier_choices = ( - ('doi', 'DOI'), - ('uri', 'URI Path'), - ('pubid', 'Publisher ID'), + ("doi", "DOI"), + ("uri", "URI Path"), + ("pubid", "Publisher ID"), ) -IDENTIFIER_TYPES = { - 'uri', - 'pubid', - 'id', - 'doi' -} +IDENTIFIER_TYPES = {"uri", "pubid", "id", "doi"} NON_DOI_IDENTIFIER_TYPES = IDENTIFIER_TYPES - {"doi"} -DOI_REGEX_PATTERN = '10.\d{4,9}/[-._;()/:A-Za-z0-9]+' -URL_DOI_REGEX_PATTERN = 'https://doi.org/10.\d{4,9}/[-._;()/:A-Za-z0-9]+' -PUB_ID_REGEX_PATTERN = '[\w-]+' +DOI_REGEX_PATTERN = "10.\d{4,9}/[-._;()/:A-Za-z0-9]+" +URL_DOI_REGEX_PATTERN = "https://doi.org/10.\d{4,9}/[-._;()/:A-Za-z0-9]+" +PUB_ID_REGEX_PATTERN = "[\w-]+" PUB_ID_RE = re.compile("^{}$".format(PUB_ID_REGEX_PATTERN)) DOI_RE = re.compile(DOI_REGEX_PATTERN) URL_DOI_RE = re.compile(URL_DOI_REGEX_PATTERN) @@ -62,7 +57,7 @@ class CrossrefDeposit(models.Model): document = models.TextField( null=True, blank=True, - help_text='The deposit document with rendered XML for the DOI batch.' + help_text="The deposit document with rendered XML for the DOI batch.", ) has_result = models.BooleanField(default=False) success = models.BooleanField(default=False) @@ -82,21 +77,25 @@ class CrossrefDeposit(models.Model): ) class Meta: - ordering = ('-date_time',) + ordering = ("-date_time",) @property @cache(30) def journal(self): - journals = set([crossref_status.identifier.article.journal for crossref_status in self.crossrefstatus_set.all()]) + journals = set( + [ + crossref_status.identifier.article.journal + for crossref_status in self.crossrefstatus_set.all() + ] + ) if len(journals) > 1: - error = f'Identifiers from multiple journals passed to CrossrefDeposit: {journals}' + error = f"Identifiers from multiple journals passed to CrossrefDeposit: {journals}" logger.debug(error) elif len(journals) == 1: return journals.pop() else: return None - def poll(self): self.polling_attempts += 1 self.save() @@ -104,70 +103,81 @@ def poll(self): test_mode, username, password = logic.get_poll_settings(self.journal) if test_mode: - test_var = 'test' + test_var = "test" else: - test_var = 'doi' + test_var = "doi" - url = 'https://{3}.crossref.org/servlet/submissionDownload?usr={0}&pwd={1}&file_name={2}.xml&type=result'.format(username, password, self.file_name, test_var) + url = "https://{3}.crossref.org/servlet/submissionDownload?usr={0}&pwd={1}&file_name={2}.xml&type=result".format( + username, password, self.file_name, test_var + ) try: response = requests.get(url, timeout=settings.HTTP_TIMEOUT_SECONDS) response.raise_for_status() self.result_text = response.text self.has_result = ' status="unknown_submission"' not in self.result_text - self.queued = 'status="queued"' in self.result_text or 'in_process' in self.result_text - self.success = '0' in self.result_text and not 'status="queued"' in self.result_text + self.queued = ( + 'status="queued"' in self.result_text + or "in_process" in self.result_text + ) + self.success = ( + "0" in self.result_text + and not 'status="queued"' in self.result_text + ) self.citation_success = not ' status="error"' in self.result_text self.save() logger.debug(self) - return f'Polled ({self.journal.code})', False + return f"Polled ({self.journal.code})", False except requests.RequestException as e: self.success = False self.has_result = True - self.result_text = f'Error ({self.journal.code}): {e}' + self.result_text = f"Error ({self.journal.code}): {e}" self.save() logger.error(self.result_text) logger.error(self) - return f'Error ({self.journal.code})', True + return f"Error ({self.journal.code})", True def get_record_diagnostic(self, doi): - soup = BeautifulSoup(self.result_text, 'lxml-xml') - record_diagnostics = soup.find_all('record_diagnostic') + soup = BeautifulSoup(self.result_text, "lxml-xml") + record_diagnostics = soup.find_all("record_diagnostic") for record_diagnostic in record_diagnostics: if record_diagnostic.doi and record_diagnostic.doi.string: if doi in record_diagnostic.doi.string: return str(record_diagnostic) def __str__(self): - return ("[Deposit:{self.file_name}]" + return ( + "[Deposit:{self.file_name}]" "[queued:{self.queued}]" "[success:{self.success}]" "[citation_success:{citation_success}]".format( self=self, - #Citation success only to be considered for succesful deposits + # Citation success only to be considered for succesful deposits citation_success=self.citation_success if self.success else None, ) ) class CrossrefStatus(models.Model): - - UNTRIED = 'untried' - QUEUED = 'queued' - REGISTERED = 'registered' - REGISTERED_BUT_CITATION_PROBLEMS = 'registered_but_citation_problems' - WARNING = 'warning' - FAILED = 'failed' - UNKNOWN = '' + UNTRIED = "untried" + QUEUED = "queued" + REGISTERED = "registered" + REGISTERED_BUT_CITATION_PROBLEMS = "registered_but_citation_problems" + WARNING = "warning" + FAILED = "failed" + UNKNOWN = "" CROSSREF_STATUS_CHOICES = ( - (UNTRIED, 'Not yet registered'), - (QUEUED, 'Queued at Crossref'), - (REGISTERED, 'Registered'), - (REGISTERED_BUT_CITATION_PROBLEMS, 'Registered (but some citations not correctly parsed)'), - (WARNING, 'Registered with warning'), - (FAILED, 'Registration failed'), - (UNKNOWN, 'Unknown'), + (UNTRIED, "Not yet registered"), + (QUEUED, "Queued at Crossref"), + (REGISTERED, "Registered"), + ( + REGISTERED_BUT_CITATION_PROBLEMS, + "Registered (but some citations not correctly parsed)", + ), + (WARNING, "Registered with warning"), + (FAILED, "Registration failed"), + (UNKNOWN, "Unknown"), ) identifier = models.OneToOneField( @@ -180,9 +190,9 @@ class CrossrefStatus(models.Model): ) message = models.CharField( blank=True, - default='Unknown', + default="Unknown", max_length=255, - help_text='A user-friendly message about the status of registration with Crossref.', + help_text="A user-friendly message about the status of registration with Crossref.", choices=CROSSREF_STATUS_CHOICES, ) @@ -214,22 +224,21 @@ def update(self): self.save() - def get_granular_status(self): doi = self.identifier.identifier try: record_diagnostic = self.latest_deposit.get_record_diagnostic(doi) if record_diagnostic: - soup = BeautifulSoup(record_diagnostic, 'lxml-xml') + soup = BeautifulSoup(record_diagnostic, "lxml-xml") tag = soup.record_diagnostic - if tag['status'] == 'Success': + if tag["status"] == "Success": return self.REGISTERED - elif tag['status'] == 'Warning': + elif tag["status"] == "Warning": return self.WARNING else: return self.FAILED except AttributeError: - return '' + return "" @property def latest_deposit(self): @@ -237,14 +246,15 @@ def latest_deposit(self): def __str__(self): if self.latest_deposit: - return f'{self.identifier} {self.message} ' \ - f'{self.latest_deposit.date_time}' + return ( + f"{self.identifier} {self.message} " f"{self.latest_deposit.date_time}" + ) else: return self.message class Meta: - verbose_name = 'Crossref status' - verbose_name_plural = 'Crossref statuses' + verbose_name = "Crossref status" + verbose_name_plural = "Crossref statuses" class Identifier(models.Model): @@ -254,7 +264,7 @@ class Identifier(models.Model): article = models.ForeignKey("submission.Article", on_delete=models.CASCADE) def __str__(self): - return u'[{0}]: {1}'.format(self.id_type.upper(), self.identifier) + return "[{0}]: {1}".format(self.id_type.upper(), self.identifier) def register(self): if self.is_doi: @@ -265,13 +275,13 @@ def register(self): def get_doi_url(self): if self.is_doi: - return 'https://doi.org/{0}'.format(self.identifier) + return "https://doi.org/{0}".format(self.identifier) else: - return 'This identifier is not a DOI.' + return "This identifier is not a DOI." @property def is_doi(self): - if self.id_type == 'doi': + if self.id_type == "doi": return True return False @@ -279,7 +289,7 @@ def is_doi(self): class BrokenDOI(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) identifier = models.ForeignKey( diff --git a/src/identifiers/tests/test_forms.py b/src/identifiers/tests/test_forms.py index 1ae0f0effa..c36356dbc4 100644 --- a/src/identifiers/tests/test_forms.py +++ b/src/identifiers/tests/test_forms.py @@ -6,10 +6,8 @@ class TestForms(TestCase): - @classmethod def setUpTestData(cls): - # Create press and journals cls.press = helpers.create_press() cls.press.save() @@ -17,13 +15,15 @@ def setUpTestData(cls): # Configure settings for journal in [cls.journal_one, cls.journal_two]: - save_setting('general', 'journal_issn', journal, '1234-5678') - save_setting('general', 'print_issn', journal, '8765-4321') - save_setting('Identifiers', 'use_crossref', journal, True) - save_setting('Identifiers', 'crossref_prefix', journal, '10.0000') - save_setting('Identifiers', 'crossref_email', journal, 'sample_email@example.com') - save_setting('Identifiers', 'crossref_name', journal, 'Journal Name') - save_setting('Identifiers', 'crossref_registrant', journal, 'registrant') + save_setting("general", "journal_issn", journal, "1234-5678") + save_setting("general", "print_issn", journal, "8765-4321") + save_setting("Identifiers", "use_crossref", journal, True) + save_setting("Identifiers", "crossref_prefix", journal, "10.0000") + save_setting( + "Identifiers", "crossref_email", journal, "sample_email@example.com" + ) + save_setting("Identifiers", "crossref_name", journal, "Journal Name") + save_setting("Identifiers", "crossref_registrant", journal, "registrant") cls.article_one = helpers.create_article(cls.journal_one, with_author=True) cls.article_two = helpers.create_article(cls.journal_two, with_author=True) @@ -32,15 +32,13 @@ def setUpTestData(cls): def test_add_doi(self): form = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/1234', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/1234", + "enabled": True, }, article=self.article_one, ) - self.assertTrue( - form.is_valid() - ) + self.assertTrue(form.is_valid()) def test_duplicate_doi_article(self): """ @@ -48,17 +46,17 @@ def test_duplicate_doi_article(self): """ form_one = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/1234', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/1234", + "enabled": True, }, article=self.article_one, ) form_two = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/1234', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/1234", + "enabled": True, }, article=self.article_one, ) @@ -74,17 +72,17 @@ def test_duplicate_doi_journal(self): """ form_one = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/1234', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/1234", + "enabled": True, }, article=self.article_one, ) form_two = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/1234', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/1234", + "enabled": True, }, article=self.article_two, ) @@ -100,17 +98,17 @@ def test_other_ident_article(self): """ form_one = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_one, ) form_two = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_one, ) @@ -126,17 +124,17 @@ def test_other_ident_journal(self): """ form_one = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_one, ) form_two = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_three, ) @@ -152,17 +150,17 @@ def test_other_ident_two_journals(self): """ form_one = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_one, ) form_two = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': '1234', - 'enabled': True, + "id_type": "pubid", + "identifier": "1234", + "enabled": True, }, article=self.article_two, ) @@ -177,16 +175,16 @@ def test_edit_existing_ident(self): Tests that we can edit an existing identifier. """ ident = models.Identifier.objects.create( - id_type='pubid', - identifier='xyz', + id_type="pubid", + identifier="xyz", enabled=True, article=self.article_one, ) form = forms.IdentifierForm( { - 'id_type': 'pubid', - 'identifier': 'xyz', - 'enabled': False, + "id_type": "pubid", + "identifier": "xyz", + "enabled": False, }, instance=ident, article=self.article_one, @@ -200,22 +198,22 @@ def test_edit_existing_ident_duplicate(self): Checks that we cannot save an existing identifier that matches another. """ ident_one = models.Identifier.objects.create( - id_type='doi', - identifier='10.1234/1234', + id_type="doi", + identifier="10.1234/1234", enabled=True, article=self.article_one, ) ident_two = models.Identifier.objects.create( - id_type='doi', - identifier='10.1234/9876', + id_type="doi", + identifier="10.1234/9876", enabled=True, article=self.article_two, ) form = forms.IdentifierForm( { - 'id_type': 'doi', - 'identifier': '10.1234/9876', - 'enabled': True, + "id_type": "doi", + "identifier": "10.1234/9876", + "enabled": True, }, instance=ident_one, article=self.article_one, @@ -223,6 +221,3 @@ def test_edit_existing_ident_duplicate(self): self.assertFalse( form.is_valid(), ) - - - diff --git a/src/identifiers/tests/test_logic.py b/src/identifiers/tests/test_logic.py index c5a4ab361f..c209d767bf 100644 --- a/src/identifiers/tests/test_logic.py +++ b/src/identifiers/tests/test_logic.py @@ -20,11 +20,11 @@ from lxml import etree from bs4 import BeautifulSoup import requests -class TestLogic(TestCase): + +class TestLogic(TestCase): @classmethod def setUpTestData(cls): - # Create press and journals cls.press = helpers.create_press() cls.press.save() @@ -32,13 +32,15 @@ def setUpTestData(cls): # Configure settings for journal in [cls.journal_one, cls.journal_two]: - save_setting('general', 'journal_issn', journal, '1234-5678') - save_setting('general', 'print_issn', journal, '8765-4321') - save_setting('Identifiers', 'use_crossref', journal, True) - save_setting('Identifiers', 'crossref_prefix', journal, '10.0000') - save_setting('Identifiers', 'crossref_email', journal, 'sample_email@example.com') - save_setting('Identifiers', 'crossref_name', journal, 'Journal Name') - save_setting('Identifiers', 'crossref_registrant', journal, 'registrant') + save_setting("general", "journal_issn", journal, "1234-5678") + save_setting("general", "print_issn", journal, "8765-4321") + save_setting("Identifiers", "use_crossref", journal, True) + save_setting("Identifiers", "crossref_prefix", journal, "10.0000") + save_setting( + "Identifiers", "crossref_email", journal, "sample_email@example.com" + ) + save_setting("Identifiers", "crossref_name", journal, "Journal Name") + save_setting("Identifiers", "crossref_registrant", journal, "registrant") # Make mock request cls.request = helpers.Request() @@ -49,7 +51,9 @@ def setUpTestData(cls): cls.issue_five_three = helpers.create_issue(cls.journal_one, vol=5, number=3) cls.article_one = helpers.create_article(cls.journal_one, with_author=True) - cls.article_published = helpers.create_article(cls.journal_one, with_author=True) + cls.article_published = helpers.create_article( + cls.journal_one, with_author=True + ) cls.article_published.stage = submission_models.STAGE_PUBLISHED cls.article_published.date_published = timezone.now() cls.article_published.save() @@ -57,12 +61,12 @@ def setUpTestData(cls): cls.doi_one = logic.generate_crossref_doi_with_pattern(cls.article_one) cls.issue_five_three.articles.add(cls.article_one) cls.article_one.primary_issue = cls.issue_five_three - cls.article_one.abstract = 'Test abstract.' + cls.article_one.abstract = "Test abstract." cls.article_one.snapshot_authors() cls.article_one.license = submission_models.Licence.objects.filter( journal=cls.journal_one, ).first() - cls.article_one.page_numbers = '1-72' + cls.article_one.page_numbers = "1-72" cls.article_one.save() cls.issue_five_three.save() @@ -79,9 +83,9 @@ def setUpTestData(cls): cls.article_three = helpers.create_article(cls.journal_one, with_author=True) doi_options = { - 'id_type': 'doi', - 'identifier': '10.1234/custom', - 'article': cls.article_three, + "id_type": "doi", + "identifier": "10.1234/custom", + "article": cls.article_three, } cls.doi_three = models.Identifier.objects.create(**doi_options) cls.issue_six_one.articles.add(cls.article_three) @@ -91,14 +95,14 @@ def setUpTestData(cls): # But issue 6.1 also has another couple articles that should be registered individually # because they have special attributes cls.article_four = helpers.create_article(cls.journal_one, with_author=True) - cls.article_four.ISSN_override = '5555-5555' + cls.article_four.ISSN_override = "5555-5555" cls.doi_four = logic.generate_crossref_doi_with_pattern(cls.article_four) cls.issue_six_one.articles.add(cls.article_four) cls.article_four.primary_issue = cls.issue_six_one cls.article_four.save() cls.article_five = helpers.create_article(cls.journal_one, with_author=True) - cls.article_five.publication_title = 'A Very Special Old Publication' + cls.article_five.publication_title = "A Very Special Old Publication" cls.doi_five = logic.generate_crossref_doi_with_pattern(cls.article_five) cls.issue_six_one.articles.add(cls.article_five) cls.article_five.primary_issue = cls.issue_six_one @@ -106,7 +110,6 @@ def setUpTestData(cls): cls.issue_six_one.save() - # Journal 2 is a conference cls.journal_two.is_conference = True cls.issue_nine_nine = helpers.create_issue(cls.journal_two, vol=9, number=9) @@ -115,12 +118,12 @@ def setUpTestData(cls): cls.doi_six = logic.generate_crossref_doi_with_pattern(cls.article_six) cls.issue_nine_nine.articles.add(cls.article_six) cls.article_six.primary_issue = cls.issue_nine_nine - cls.article_six.abstract = 'Test abstract.' + cls.article_six.abstract = "Test abstract." cls.article_six.snapshot_authors() cls.article_six.license = submission_models.Licence.objects.filter( journal=cls.journal_two, ).first() - cls.article_six.page_numbers = '58-62' + cls.article_six.page_numbers = "58-62" cls.article_six.save() cls.article_seven = helpers.create_article(cls.journal_two, with_author=True) @@ -131,55 +134,56 @@ def setUpTestData(cls): cls.issue_nine_nine.save() - # Schema location for Crossref XML validation cls.schema_base_path = os.path.join( settings.BASE_DIR, - 'identifiers', - 'tests', - 'test_data', - 'schemas', + "identifiers", + "tests", + "test_data", + "schemas", ) def test_create_crossref_doi_batch_context(self): self.maxDiff = None expected_data = {} - expected_data['depositor_email'] = 'sample_email@example.com' - expected_data['depositor_name'] = 'Journal Name' - expected_data['registrant'] = 'registrant' - expected_data['is_conference'] = self.journal_one.is_conference + expected_data["depositor_email"] = "sample_email@example.com" + expected_data["depositor_name"] = "Journal Name" + expected_data["registrant"] = "registrant" + expected_data["is_conference"] = self.journal_one.is_conference context = logic.create_crossref_doi_batch_context( - self.journal_one, - set([self.doi_one]) + self.journal_one, set([self.doi_one]) ) # A couple things need to be adjusted with context for test to work - for key in ['batch_id', 'now', 'timestamp', 'timestamp_suffix']: + for key in ["batch_id", "now", "timestamp", "timestamp_suffix"]: context.pop(key) # Don't test lower levels of nested context in this test - expected_data['crossref_issues'] = [] - if 'crossref_issues' in context: - context['crossref_issues'] = [] + expected_data["crossref_issues"] = [] + if "crossref_issues" in context: + context["crossref_issues"] = [] self.assertEqual(expected_data, context) - def test_create_crossef_issues_context(self): # Just expect the right number of crossref_issues # each with the right keys expected_data = [ - {'journal':None,'issue':None,'articles':None} for x in range(4) + {"journal": None, "issue": None, "articles": None} for x in range(4) ] identifiers = set() - identifiers.add(self.doi_one) # Should be on its own in 5.3 - identifiers.add(self.doi_two) # Should go together with below in 6.1 - identifiers.add(self.doi_three) # Should go together with above in 6.1 - identifiers.add(self.doi_four) # Should be on its own due to Article.ISSN_override - identifiers.add(self.doi_five) # Should be on its own due to Article.publication_title + identifiers.add(self.doi_one) # Should be on its own in 5.3 + identifiers.add(self.doi_two) # Should go together with below in 6.1 + identifiers.add(self.doi_three) # Should go together with above in 6.1 + identifiers.add( + self.doi_four + ) # Should be on its own due to Article.ISSN_override + identifiers.add( + self.doi_five + ) # Should be on its own due to Article.publication_title context = logic.create_crossref_issues_context( self.journal_one, identifiers, @@ -192,7 +196,6 @@ def test_create_crossef_issues_context(self): self.assertEqual(expected_data, context) - def test_create_crossref_issue_context(self): expected_issue = self.issue_five_three expected_number_of_articles = 1 @@ -201,8 +204,8 @@ def test_create_crossref_issue_context(self): set([self.doi_one]), self.issue_five_three, ) - self.assertEqual(expected_issue, context['issue']) - self.assertEqual(expected_number_of_articles, len(context['articles'])) + self.assertEqual(expected_issue, context["issue"]) + self.assertEqual(expected_number_of_articles, len(context["articles"])) expected_issue = self.issue_six_one expected_number_of_articles = 2 @@ -211,101 +214,111 @@ def test_create_crossref_issue_context(self): set([self.doi_two, self.doi_three]), self.issue_six_one, ) - self.assertEqual(expected_issue, context['issue']) - self.assertEqual(expected_number_of_articles, len(context['articles'])) - + self.assertEqual(expected_issue, context["issue"]) + self.assertEqual(expected_number_of_articles, len(context["articles"])) def test_create_crossref_journal_context(self): expected_data = { - 'title': 'Journal One', - 'journal_issn': '1234-5678', - 'print_issn': '8765-4321', - 'press': self.journal_one.press + "title": "Journal One", + "journal_issn": "1234-5678", + "print_issn": "8765-4321", + "press": self.journal_one.press, } context = logic.create_crossref_journal_context(self.journal_one) self.assertEqual(expected_data, context) - def test_create_crossref_article_context_published(self): self.maxDiff = None expected_data = { - 'title': self.article_published.title, - 'abstract': '', - 'url': self.article_published.url, - 'authors': [ + "title": self.article_published.title, + "abstract": "", + "url": self.article_published.url, + "authors": [ author.email for author in self.article_published.frozenauthor_set.all() ], - 'citation_list': None, - 'date_accepted': None, - 'date_published': self.article_published.date_published, - 'doi': f'10.0000/TST.{self.article_published.id}', - 'id': self.article_published.id, - 'license': '', - 'pages': self.article_published.page_numbers, - 'scheduled': True, + "citation_list": None, + "date_accepted": None, + "date_published": self.article_published.date_published, + "doi": f"10.0000/TST.{self.article_published.id}", + "id": self.article_published.id, + "license": "", + "pages": self.article_published.page_numbers, + "scheduled": True, } context = logic.create_crossref_article_context(self.article_published) - context['authors'] = [author.email for author in context['authors']] + context["authors"] = [author.email for author in context["authors"]] self.assertEqual(expected_data, context) def test_create_crossref_article_context_not_published(self): expected_data = { - 'title': self.article_one.title, - 'abstract': self.article_one.abstract, - 'url': self.article_one.url, - 'authors': [ + "title": self.article_one.title, + "abstract": self.article_one.abstract, + "url": self.article_one.url, + "authors": [ author.email for author in self.article_one.frozenauthor_set.all() ], - 'citation_list': None, - 'date_accepted': None, - 'date_published': None, - 'doi': self.doi_one.identifier, - 'id': self.article_one.id, - 'license': submission_models.Licence.objects.filter( + "citation_list": None, + "date_accepted": None, + "date_published": None, + "doi": self.doi_one.identifier, + "id": self.article_one.id, + "license": submission_models.Licence.objects.filter( journal=self.journal_one, - ).first().url, - 'pages': self.article_one.page_numbers, - 'scheduled': False, + ) + .first() + .url, + "pages": self.article_one.page_numbers, + "scheduled": False, } context = logic.create_crossref_article_context(self.article_one) - context['authors'] = [author.email for author in context['authors']] + context["authors"] = [author.email for author in context["authors"]] self.assertEqual(expected_data, context) def test_preview_registration_information_when_use_crossref_on(self): clear_cache() - save_setting('Identifiers', 'use_crossref', self.journal_one, True) - self.assertTrue('Current metadata to send to Crossref' in self.article_one.registration_preview) + save_setting("Identifiers", "use_crossref", self.journal_one, True) + self.assertTrue( + "Current metadata to send to Crossref" + in self.article_one.registration_preview + ) def test_preview_registration_information_when_use_crossref_off(self): clear_cache() - save_setting('Identifiers', 'use_crossref', self.journal_one, False) + save_setting("Identifiers", "use_crossref", self.journal_one, False) self.assertFalse(self.article_one.registration_preview) def test_preview_registration_information_when_custom_doi(self): clear_cache() - self.assertTrue(self.doi_three.identifier in self.article_three.registration_preview) + self.assertTrue( + self.doi_three.identifier in self.article_three.registration_preview + ) def test_deposit_xml_document_is_valid(self): self.maxDiff = None for journal in [self.journal_one, self.journal_two]: # Generate batch and validate against schema - template = 'common/identifiers/crossref_doi_batch.xml' - identifiers = set([identifier for identifier in models.Identifier.objects.filter( - article__journal=journal - )]) + template = "common/identifiers/crossref_doi_batch.xml" + identifiers = set( + [ + identifier + for identifier in models.Identifier.objects.filter( + article__journal=journal + ) + ] + ) template_context = logic.create_crossref_doi_batch_context( journal, identifiers, ) deposit = logic.render_to_string(template, template_context) - soup = BeautifulSoup(deposit, 'lxml') - version = soup.find('doi_batch')['version'] - schema_filename = f'crossref{version}.xsd' + soup = BeautifulSoup(deposit, "lxml") + version = soup.find("doi_batch")["version"] + schema_filename = f"crossref{version}.xsd" with open(os.path.join(self.schema_base_path, schema_filename)) as fileref: xml_schema_doc = etree.parse(fileref) xml_schema = etree.XMLSchema(xml_schema_doc) @@ -316,47 +329,57 @@ def test_deposit_xml_document_is_valid(self): self.assertTrue(xml_schema.validate(doc)) def test_journal_deposit_xml_document_has_basically_correct_components(self): - template = 'common/identifiers/crossref_doi_batch.xml' - identifiers = set([identifier for identifier in models.Identifier.objects.filter( - article__journal=self.journal_one - )]) + template = "common/identifiers/crossref_doi_batch.xml" + identifiers = set( + [ + identifier + for identifier in models.Identifier.objects.filter( + article__journal=self.journal_one + ) + ] + ) template_context = logic.create_crossref_doi_batch_context( self.journal_one, identifiers, ) deposit = logic.render_to_string(template, template_context) - soup = BeautifulSoup(deposit, 'lxml') + soup = BeautifulSoup(deposit, "lxml") # There should be one doi_batch - self.assertEqual(1, len(soup.find_all('doi_batch'))) + self.assertEqual(1, len(soup.find_all("doi_batch"))) # There should be four crossref_issues - self.assertEqual(4, len(soup.find_all('journal_issue'))) + self.assertEqual(4, len(soup.find_all("journal_issue"))) # And so journal metadata should appear four times, once for each issue - self.assertEqual(4, len(soup.find_all('journal_metadata'))) + self.assertEqual(4, len(soup.find_all("journal_metadata"))) # There should be five articles - self.assertEqual(5, len(soup.find_all('journal_article'))) + self.assertEqual(5, len(soup.find_all("journal_article"))) def test_conference_deposit_xml_document_has_basically_correct_components(self): - template = 'common/identifiers/crossref_doi_batch.xml' - identifiers = set([identifier for identifier in models.Identifier.objects.filter( - article__journal=self.journal_two - )]) + template = "common/identifiers/crossref_doi_batch.xml" + identifiers = set( + [ + identifier + for identifier in models.Identifier.objects.filter( + article__journal=self.journal_two + ) + ] + ) template_context = logic.create_crossref_doi_batch_context( self.journal_two, identifiers, ) deposit = logic.render_to_string(template, template_context) - soup = BeautifulSoup(deposit, 'lxml') + soup = BeautifulSoup(deposit, "lxml") # There should be one doi_batch - self.assertEqual(1, len(soup.find_all('doi_batch'))) + self.assertEqual(1, len(soup.find_all("doi_batch"))) # There should be one conference (crossref_issue) - self.assertEqual(1, len(soup.find_all('conference'))) + self.assertEqual(1, len(soup.find_all("conference"))) # And so conference metadata should appear one time, once for each issue - self.assertEqual(1, len(soup.find_all('event_metadata'))) + self.assertEqual(1, len(soup.find_all("event_metadata"))) # There should be two conference papers (articles) - self.assertEqual(2, len(soup.find_all('conference_paper'))) + self.assertEqual(2, len(soup.find_all("conference_paper"))) def test_issue_doi_deposited_correctly(self): - template = 'common/identifiers/crossref_doi_batch.xml' + template = "common/identifiers/crossref_doi_batch.xml" issue = self.article_one.issue issue.doi = issue_doi = "10.0001/issue" issue.save() @@ -369,9 +392,9 @@ def test_issue_doi_deposited_correctly(self): ) deposit = logic.render_to_string(template, template_context) - soup = BeautifulSoup(deposit, 'lxml') + soup = BeautifulSoup(deposit, "lxml") # There should be one doi_batch - issue_soup = soup.find('journal_issue') + issue_soup = soup.find("journal_issue") found = False if issue_soup: issue_doi_soup = issue_soup.find("doi_data") @@ -383,10 +406,10 @@ def test_issue_doi_deposited_correctly(self): raise AssertionError("No Issue DOI found on article deposit") def test_journal_doi_deposited_correctly(self): - template = 'common/identifiers/crossref_doi_batch.xml' + template = "common/identifiers/crossref_doi_batch.xml" issue = self.article_one.issue journal_doi = "10.0001/journal" - save_setting('Identifiers', 'title_doi', issue.journal, journal_doi) + save_setting("Identifiers", "title_doi", issue.journal, journal_doi) identifier = self.article_one.get_doi_object clear_cache() @@ -396,16 +419,17 @@ def test_journal_doi_deposited_correctly(self): ) deposit = logic.render_to_string(template, template_context) - soup = BeautifulSoup(deposit, 'lxml') + soup = BeautifulSoup(deposit, "lxml") # There should be one doi_batch - journal_soup = soup.find('journal_metadata') + journal_soup = soup.find("journal_metadata") found = False if journal_soup: doi_soup = journal_soup.find("doi_data") if doi_soup: self.assertEqual(doi_soup.find("doi").string, journal_doi) self.assertEqual( - doi_soup.find("resource").string, issue.journal.site_url()) + doi_soup.find("resource").string, issue.journal.site_url() + ) found = True if not found: raise AssertionError("No Issue DOI found on article deposit") @@ -413,10 +437,11 @@ def test_journal_doi_deposited_correctly(self): def test_issue_doi_auto_assign_enabled(self): issue = helpers.create_issue(self.journal_one, vol=99, number=99) self.request.POST = {"assign_issue": issue.pk} - mock_messages = mock.patch('journal.logic.messages').start() + mock_messages = mock.patch("journal.logic.messages").start() mock_messages.messages = mock.MagicMock() - save_setting('Identifiers', 'register_issue_dois', self.journal_one, 'on') - from events import registration # Forces events to load into memory + save_setting("Identifiers", "register_issue_dois", self.journal_one, "on") + from events import registration # Forces events to load into memory + journal_logic.handle_assign_issue(self.request, self.article_one, issue) issue.refresh_from_db() self.assertTrue(issue.doi) @@ -424,27 +449,30 @@ def test_issue_doi_auto_assign_enabled(self): def test_issue_doi_auto_assigned_disabled(self): issue = helpers.create_issue(self.journal_one, vol=99, number=99) self.request.POST = {"assign_issue": issue.pk} - mock_messages = mock.patch('journal.logic.messages').start() + mock_messages = mock.patch("journal.logic.messages").start() mock_messages.messages = mock.MagicMock() - save_setting('Identifiers', 'register_issue_dois', self.journal_one, '') - from events import registration # Forces events to load into memory + save_setting("Identifiers", "register_issue_dois", self.journal_one, "") + from events import registration # Forces events to load into memory + journal_logic.handle_assign_issue(self.request, self.article_one, issue) issue.refresh_from_db() self.assertEqual(issue.doi, None) def test_check_crossref_settings(self): - # Missing settings - save_setting('Identifiers', 'crossref_prefix', self.journal_one, '') - save_setting('Identifiers', 'crossref_username', self.journal_one, '') - save_setting('Identifiers', 'crossref_password', self.journal_one, '') + save_setting("Identifiers", "crossref_prefix", self.journal_one, "") + save_setting("Identifiers", "crossref_username", self.journal_one, "") + save_setting("Identifiers", "crossref_password", self.journal_one, "") use_crossref, test_mode, missing_settings = logic.check_crossref_settings( self.journal_one ) # Put need missing setting back - save_setting('Identifiers', 'crossref_prefix', self.journal_one, '10.0000') + save_setting("Identifiers", "crossref_prefix", self.journal_one, "10.0000") self.assertEqual(use_crossref, True) - self.assertEqual(missing_settings, ['crossref_prefix', 'crossref_username', 'crossref_password']) + self.assertEqual( + missing_settings, + ["crossref_prefix", "crossref_username", "crossref_password"], + ) diff --git a/src/identifiers/tests/test_models.py b/src/identifiers/tests/test_models.py index e8debbf318..3f1b63b0c8 100644 --- a/src/identifiers/tests/test_models.py +++ b/src/identifiers/tests/test_models.py @@ -7,48 +7,49 @@ class TestLogic(TestCase): - @classmethod def setUpTestData(cls): - cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() from utils.setting_handler import save_setting - save_setting('general', 'journal_issn', cls.journal_one, '1234-5678') - save_setting('general', 'print_issn', cls.journal_one, '8765-4321') - save_setting('Identifiers', 'use_crossref', cls.journal_one, True) - save_setting('Identifiers', 'crossref_prefix', cls.journal_one, '10.0000') + + save_setting("general", "journal_issn", cls.journal_one, "1234-5678") + save_setting("general", "print_issn", cls.journal_one, "8765-4321") + save_setting("Identifiers", "use_crossref", cls.journal_one, True) + save_setting("Identifiers", "crossref_prefix", cls.journal_one, "10.0000") cls.ten_articles = [helpers.create_article(cls.journal_one) for i in range(10)] cls.ten_identifiers = logic.get_dois_for_articles(cls.ten_articles, create=True) - def test_crossref_deposit(self): - template = 'common/identifiers/crossref_doi_batch.xml' + template = "common/identifiers/crossref_doi_batch.xml" template_context = logic.create_crossref_doi_batch_context( - self.journal_one, - set(self.ten_identifiers) + self.journal_one, set(self.ten_identifiers) ) document = logic.render_to_string(template, template_context) filename = uuid4() - crossref_deposit = models.CrossrefDeposit.objects.create(document=document, file_name=filename) + crossref_deposit = models.CrossrefDeposit.objects.create( + document=document, file_name=filename + ) crossref_deposit.save() for identifier in self.ten_identifiers: self.assertTrue(identifier.identifier in crossref_deposit.document) - def test_crossref_status(self): - template = 'common/identifiers/crossref_doi_batch.xml' + template = "common/identifiers/crossref_doi_batch.xml" template_context = logic.create_crossref_doi_batch_context( - self.journal_one, - set(self.ten_identifiers) + self.journal_one, set(self.ten_identifiers) ) document = logic.render_to_string(template, template_context) filename = uuid4() - crossref_deposit = models.CrossrefDeposit.objects.create(document=document, file_name=filename) + crossref_deposit = models.CrossrefDeposit.objects.create( + document=document, file_name=filename + ) crossref_deposit.save() identifier = self.ten_identifiers[0] - status, _created = models.CrossrefStatus.objects.get_or_create(identifier=identifier) + status, _created = models.CrossrefStatus.objects.get_or_create( + identifier=identifier + ) status.deposits.add(crossref_deposit) status.update() - self.assertTrue('Unknown', status) + self.assertTrue("Unknown", status) diff --git a/src/identifiers/urls.py b/src/identifiers/urls.py index 485d8b9064..92313df2f4 100755 --- a/src/identifiers/urls.py +++ b/src/identifiers/urls.py @@ -7,35 +7,52 @@ from identifiers import views urlpatterns = [ - re_path(r'^pingback$', views.pingback, name='crossref_pingback'), - re_path(r'^(?P\d+)/$', - views.article_identifiers, - name='article_identifiers'), - re_path(r'^(?P\d+)/$', - views.article_identifiers, - name='edit_identifiers'), - re_path(r'^(?P\d+)/new/$', + re_path(r"^pingback$", views.pingback, name="crossref_pingback"), + re_path( + r"^(?P\d+)/$", views.article_identifiers, name="article_identifiers" + ), + re_path( + r"^(?P\d+)/$", views.article_identifiers, name="edit_identifiers" + ), + re_path( + r"^(?P\d+)/new/$", views.manage_identifier, - name='add_new_identifier'), - re_path(r'^(?P\d+)/edit/(?P\d+)/$', + name="add_new_identifier", + ), + re_path( + r"^(?P\d+)/edit/(?P\d+)/$", views.manage_identifier, - name='edit_identifier'), - re_path(r'^(?P\d+)/delete/(?P\d+)/$', + name="edit_identifier", + ), + re_path( + r"^(?P\d+)/delete/(?P\d+)/$", views.delete_identifier, - name='delete_identifier'), - re_path(r'^(?P\d+)/issue/(?P\d+)/$', + name="delete_identifier", + ), + re_path( + r"^(?P\d+)/issue/(?P\d+)/$", views.issue_doi, - name='issue_doi'), - re_path(r'^(?P\d+)/show/(?P\d+)/$', + name="issue_doi", + ), + re_path( + r"^(?P\d+)/show/(?P\d+)/$", views.show_doi, - name='show_doi'), - re_path(r'^(?P\d+)/poll/(?P\d+)/$', + name="show_doi", + ), + re_path( + r"^(?P\d+)/poll/(?P\d+)/$", views.poll_doi, - name='poll_doi'), - re_path(r'^(?P\d+)/poll/output/(?P\d+)/$', + name="poll_doi", + ), + re_path( + r"^(?P\d+)/poll/output/(?P\d+)/$", views.poll_doi_output, - name='poll_doi_output'), - + name="poll_doi_output", + ), # DOI Manager - re_path(r'^doi_manager/$', views.IdentifierManager.as_view(), name='journal_identifier_manager'), + re_path( + r"^doi_manager/$", + views.IdentifierManager.as_view(), + name="journal_identifier_manager", + ), ] diff --git a/src/identifiers/views.py b/src/identifiers/views.py index 871c531e42..11446afc90 100755 --- a/src/identifiers/views.py +++ b/src/identifiers/views.py @@ -31,18 +31,18 @@ def pingback(request): # TODO: not sure what Crossref will actually # send here so for now it just dumps all data - output = '' + output = "" for key, value in request.POST.items(): - output += '{0}: {1}\n'.format(key, value) + output += "{0}: {1}\n".format(key, value) util_models.LogEntry.add_entry( - 'Submission', + "Submission", "Response from Crossref pingback: {0}".format(output), - 'Info', + "Info", ) - return HttpResponse('') + return HttpResponse("") @production_user_or_editor_required @@ -60,10 +60,10 @@ def article_identifiers(request, article_id): ) identifiers = models.Identifier.objects.filter(article=article) - template = 'identifiers/article_identifiers.html' + template = "identifiers/article_identifiers.html" context = { - 'article': article, - 'identifiers': identifiers, + "article": article, + "identifiers": identifiers, } return render(request, template, context) @@ -83,11 +83,15 @@ def manage_identifier(request, article_id, identifier_id=None): pk=article_id, journal=request.journal, ) - identifier = get_object_or_404( - models.Identifier, - pk=identifier_id, - article=article, - ) if identifier_id else None + identifier = ( + get_object_or_404( + models.Identifier, + pk=identifier_id, + article=article, + ) + if identifier_id + else None + ) form = forms.IdentifierForm( instance=identifier, @@ -110,16 +114,16 @@ def manage_identifier(request, article_id, identifier_id=None): ) return redirect( reverse( - 'article_identifiers', - kwargs={'article_id': article.pk}, + "article_identifiers", + kwargs={"article_id": article.pk}, ) ) - template = 'identifiers/manage_identifier.html' + template = "identifiers/manage_identifier.html" context = { - 'article': article, - 'identifier': identifier, - 'form': form, + "article": article, + "identifier": identifier, + "form": form, } return render(request, template, context) @@ -135,6 +139,7 @@ def show_doi(request, article_id, identifier_id): :return: HttpRedirect """ from utils import setting_handler + article = get_object_or_404( submission_models.Article, pk=article_id, @@ -144,7 +149,7 @@ def show_doi(request, article_id, identifier_id): models.Identifier, pk=identifier_id, article=article, - id_type='doi', + id_type="doi", ) try: @@ -153,8 +158,10 @@ def show_doi(request, article_id, identifier_id): raise AttributeError return HttpResponse(document, content_type="application/xml") except AttributeError: - template_context = logic.create_crossref_doi_batch_context(request.journal, set([identifier])) - template = 'common/identifiers/crossref_doi_batch.xml' + template_context = logic.create_crossref_doi_batch_context( + request.journal, set([identifier]) + ) + template = "common/identifiers/crossref_doi_batch.xml" return render(None, template, template_context, content_type="application/xml") @@ -168,6 +175,7 @@ def poll_doi(request, article_id, identifier_id): :return: HttpRedirect """ from utils import setting_handler + article = get_object_or_404( submission_models.Article, pk=article_id, @@ -177,7 +185,7 @@ def poll_doi(request, article_id, identifier_id): models.Identifier, pk=identifier_id, article=article, - id_type='doi', + id_type="doi", ) # Scenario 1: The identifier has not been polled or deposited before. @@ -190,9 +198,7 @@ def poll_doi(request, article_id, identifier_id): elif identifier.crossrefstatus.latest_deposit: status, error = identifier.crossrefstatus.latest_deposit.poll() messages.add_message( - request, - messages.INFO if not error else messages.ERROR, - status + request, messages.INFO if not error else messages.ERROR, status ) # Scenario 3: The identifier has only been polled before @@ -205,8 +211,8 @@ def poll_doi(request, article_id, identifier_id): return redirect( reverse( - 'article_identifiers', - kwargs={'article_id': article.pk}, + "article_identifiers", + kwargs={"article_id": article.pk}, ) ) @@ -221,6 +227,7 @@ def poll_doi_output(request, article_id, identifier_id): :return: HttpRedirect """ from utils import setting_handler + article = get_object_or_404( submission_models.Article, pk=article_id, @@ -230,20 +237,25 @@ def poll_doi_output(request, article_id, identifier_id): models.Identifier, pk=identifier_id, article=article, - id_type='doi', + id_type="doi", ) if not identifier.crossrefstatus: - return HttpResponse('Error: no deposit found') - elif 'doi_batch' not in identifier.crossrefstatus.latest_deposit.result_text: + return HttpResponse("Error: no deposit found") + elif "doi_batch" not in identifier.crossrefstatus.latest_deposit.result_text: return HttpResponse(identifier.crossrefstatus.latest_deposit.result_text) else: - text = identifier.crossrefstatus.latest_deposit.get_record_diagnostic(identifier.identifier) + text = identifier.crossrefstatus.latest_deposit.get_record_diagnostic( + identifier.identifier + ) if text: resp = HttpResponse(text, content_type="application/xml") else: - resp = HttpResponse(identifier.crossrefstatus.latest_deposit.result_text, content_type="application/xml") - resp['Content-Disposition'] = 'inline;' + resp = HttpResponse( + identifier.crossrefstatus.latest_deposit.result_text, + content_type="application/xml", + ) + resp["Content-Disposition"] = "inline;" return resp @@ -266,20 +278,18 @@ def issue_doi(request, article_id, identifier_id): models.Identifier, pk=identifier_id, article=article, - id_type='doi', + id_type="doi", ) status, error = identifier.register() messages.add_message( - request, - messages.INFO if not error else messages.ERROR, - status + request, messages.INFO if not error else messages.ERROR, status ) return redirect( reverse( - 'article_identifiers', - kwargs={'article_id': article.pk}, + "article_identifiers", + kwargs={"article_id": article.pk}, ) ) @@ -306,64 +316,60 @@ def delete_identifier(request, article_id, identifier_id): ) identifier.delete() - messages.add_message( - request, messages.SUCCESS, - 'Identifier deleted.' - ) + messages.add_message(request, messages.SUCCESS, "Identifier deleted.") return redirect( reverse( - 'article_identifiers', - kwargs={'article_id': article.pk}, + "article_identifiers", + kwargs={"article_id": article.pk}, ) ) -@method_decorator(editor_user_required, name='dispatch') +@method_decorator(editor_user_required, name="dispatch") class IdentifierManager(journal_views.FacetedArticlesListView): - template_name = 'core/manager/identifier_manager.html' + template_name = "core/manager/identifier_manager.html" # None or integer action_queryset_chunk_size = 100 def get_facets(self): - crossref_status_obj = models.CrossrefStatus.objects.filter( - identifier__article=OuterRef('pk'), + identifier__article=OuterRef("pk"), ) - status = Subquery( - crossref_status_obj.values('message')[:1] - ) + status = Subquery(crossref_status_obj.values("message")[:1]) facets = { - 'date_published__date__gte': { - 'type': 'date', - 'field_label': 'Pub date from', + "date_published__date__gte": { + "type": "date", + "field_label": "Pub date from", }, - 'date_published__date__lte': { - 'type': 'date', - 'field_label': 'Pub date to', + "date_published__date__lte": { + "type": "date", + "field_label": "Pub date to", }, - 'status': { - 'type': 'charfield_with_choices', - 'annotations': { - 'status': status, + "status": { + "type": "charfield_with_choices", + "annotations": { + "status": status, }, - 'model_choices': models.CrossrefStatus._meta.get_field('message').choices, - 'field_label': 'Status', + "model_choices": models.CrossrefStatus._meta.get_field( + "message" + ).choices, + "field_label": "Status", }, - 'journal__pk': { - 'type': 'foreign_key', - 'model': journal_models.Journal, - 'field_label': 'Journal', - 'choice_label_field': 'name', + "journal__pk": { + "type": "foreign_key", + "model": journal_models.Journal, + "field_label": "Journal", + "choice_label_field": "name", }, - 'primary_issue__pk': { - 'type': 'foreign_key', - 'model': journal_models.Issue, - 'field_label': 'Primary issue', - 'choice_label_field': 'display_title', + "primary_issue__pk": { + "type": "foreign_key", + "model": journal_models.Issue, + "field_label": "Primary issue", + "choice_label_field": "display_title", }, } return self.filter_facets_if_journal(facets) @@ -371,13 +377,13 @@ def get_facets(self): def get_actions(self): return [ { - 'name': 'register_dois', - 'value': 'Register DOIs', - 'action': logic.register_batch_of_crossref_dois, + "name": "register_dois", + "value": "Register DOIs", + "action": logic.register_batch_of_crossref_dois, }, { - 'name': 'poll_doi_status', - 'value': 'Poll for status', - 'action': logic.poll_dois_for_articles, + "name": "poll_doi_status", + "value": "Poll for status", + "action": logic.poll_dois_for_articles, }, ] diff --git a/src/install/apps.py b/src/install/apps.py index 946979fae6..ce2ad4efcd 100755 --- a/src/install/apps.py +++ b/src/install/apps.py @@ -7,4 +7,4 @@ class InstallConfig(AppConfig): - name = 'install' + name = "install" diff --git a/src/install/forms.py b/src/install/forms.py index 7e77701d5c..9e01cca055 100755 --- a/src/install/forms.py +++ b/src/install/forms.py @@ -9,14 +9,12 @@ class JournalForm(forms.ModelForm): - class Meta: model = journal_models.Journal - fields = ('code', 'description') + fields = ("code", "description") class JournalSettingsForm(forms.Form): - journal_name = forms.CharField(label="Journal Name") publisher_name = forms.CharField(label="Publisher Name") publisher_url = forms.URLField(label="Publisher URL") @@ -24,4 +22,6 @@ class JournalSettingsForm(forms.Form): def save(self, request, commit=True): for setting_name, setting_value in self.cleaned_data.items(): - test = setting_handler.save_setting('general', setting_name, request.journal, setting_value) + test = setting_handler.save_setting( + "general", setting_name, request.journal, setting_value + ) diff --git a/src/install/logic.py b/src/install/logic.py index 832eb2b243..f30032d30e 100755 --- a/src/install/logic.py +++ b/src/install/logic.py @@ -8,7 +8,7 @@ def get_initial_settings(journal, settings_to_get): settings = {} for setting in settings_to_get: - got = setting_handler.get_setting('general', setting, journal).value + got = setting_handler.get_setting("general", setting, journal).value settings[setting] = got return settings diff --git a/src/install/migrations/0001_initial.py b/src/install/migrations/0001_initial.py index 6ffc4f84eb..a3f49c21bf 100644 --- a/src/install/migrations/0001_initial.py +++ b/src/install/migrations/0001_initial.py @@ -9,7 +9,6 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [CITextExtension()] diff --git a/src/install/urls.py b/src/install/urls.py index e8a2a5db94..b05199217e 100755 --- a/src/install/urls.py +++ b/src/install/urls.py @@ -7,7 +7,7 @@ from install import views urlpatterns = [ - re_path(r'^$', views.index, name='install_index'), - re_path(r'^journal/$', views.journal, name='install_journal'), - re_path(r'^next/$', views.next, name='install_next') + re_path(r"^$", views.index, name="install_index"), + re_path(r"^journal/$", views.journal, name="install_journal"), + re_path(r"^next/$", views.next, name="install_next"), ] diff --git a/src/install/views.py b/src/install/views.py index f697489e1f..178aa03638 100755 --- a/src/install/views.py +++ b/src/install/views.py @@ -19,14 +19,14 @@ def index(request): :return: HttpResponse or if request.POST: HttpRedirect """ if request.POST: - file = request.FILES.get('press_logo') - file = files.save_file_to_press(request, file, 'Press Logo', '') + file = request.FILES.get("press_logo") + file = files.save_file_to_press(request, file, "Press Logo", "") request.press.thumbnail_image = file request.press.save() - return redirect(reverse('install_index')) + return redirect(reverse("install_index")) - template = 'install/index.html' + template = "install/index.html" context = {} return render(request, template, context) @@ -40,8 +40,11 @@ def journal(request): :return: HttpResponse object """ settings_to_get = [ - 'journal_name', 'journal_issn', 'print_issn', - 'publisher_name', 'publisher_url', + "journal_name", + "journal_issn", + "print_issn", + "publisher_name", + "publisher_url", ] initial_items = logic.get_initial_settings(request.journal, settings_to_get) @@ -56,10 +59,10 @@ def journal(request): attr_form.save() sett_form.save(request=request) - template = 'install/journal.html' + template = "install/journal.html" context = { - 'attr_form': attr_form, - 'sett_form': sett_form, + "attr_form": attr_form, + "sett_form": sett_form, } return render(request, template, context) @@ -72,7 +75,7 @@ def next(request): :param request: HttpRequest object :return: HttpResponse object """ - template = 'install/next.html' + template = "install/next.html" context = {} return render(request, template, context) diff --git a/src/janeway/__init__.py b/src/janeway/__init__.py index cf2b40c751..f8d190a5fc 100644 --- a/src/janeway/__init__.py +++ b/src/janeway/__init__.py @@ -1,2 +1,3 @@ from packaging import version + __version__ = version.parse("1.7.2") diff --git a/src/journal/admin.py b/src/journal/admin.py index 3951eb53fc..4c14d96baf 100755 --- a/src/journal/admin.py +++ b/src/journal/admin.py @@ -11,15 +11,21 @@ class IssueAdmin(admin.ModelAdmin): - list_display = ('pk', 'issue_title', 'volume', 'issue', 'date', - 'journal', 'issue_type') - list_display_links = ('pk', 'issue_title') - list_filter = ('journal', 'date') - search_fields = ('pk', 'issue_title', 'volume', 'issue', - 'journal__code') - date_hierarchy = ('date') - filter_horizontal = ('articles',) - raw_id_fields = ('issue_type',) + list_display = ( + "pk", + "issue_title", + "volume", + "issue", + "date", + "journal", + "issue_type", + ) + list_display_links = ("pk", "issue_title") + list_filter = ("journal", "date") + search_fields = ("pk", "issue_title", "volume", "issue", "journal__code") + date_hierarchy = "date" + filter_horizontal = ("articles",) + raw_id_fields = ("issue_type",) inlines = [ admin_utils.IssueGalleyInline, @@ -29,33 +35,47 @@ class IssueAdmin(admin.ModelAdmin): class IssueTypeAdmin(admin.ModelAdmin): - list_display = ('code', 'pretty_name', 'journal') - list_filter = ('journal',) - search_fields = ('code', 'pretty_name') + list_display = ("code", "pretty_name", "journal") + list_filter = ("journal",) + search_fields = ("code", "pretty_name") class IssueGalleyAdmin(admin.ModelAdmin): - list_display = ('pk', 'file', 'issue', '_journal') - list_display_links = ('pk', 'file') - list_filter = ('issue__journal',) - search_fields = ('pk', 'file__original_filename', 'issue__journal__code', - 'issue__issue_title', 'issue__volume', 'issue__issue') + list_display = ("pk", "file", "issue", "_journal") + list_display_links = ("pk", "file") + list_filter = ("issue__journal",) + search_fields = ( + "pk", + "file__original_filename", + "issue__journal__code", + "issue__issue_title", + "issue__volume", + "issue__issue", + ) def _journal(self, obj): - return obj.issue.journal if obj else '' + return obj.issue.journal if obj else "" class IssueEditorAdmin(admin.ModelAdmin): - list_display = ('account', '_issue', '_journal', 'role') - list_filter = ('issue__journal', 'role',) - search_fields = ('account__email', 'account__first_name', - 'account__last_name', 'role', 'issue__issue_title', - 'issue__journal__code') + list_display = ("account", "_issue", "_journal", "role") + list_filter = ( + "issue__journal", + "role", + ) + search_fields = ( + "account__email", + "account__first_name", + "account__last_name", + "role", + "issue__issue_title", + "issue__journal__code", + ) - raw_id_fields = ('account', 'issue') + raw_id_fields = ("account", "issue") def _journal(self, obj): - return obj.issue.journal if obj else '' + return obj.issue.journal if obj else "" def _issue(self, obj): return truncatewords(obj.issue.__str__(), 10) @@ -63,52 +83,63 @@ def _issue(self, obj): class JournalAdmin(admin.ModelAdmin): list_display = ( - 'name', - 'code', - 'domain', - 'is_remote', - 'is_conference', - 'hide_from_press', + "name", + "code", + "domain", + "is_remote", + "is_conference", + "hide_from_press", ) - list_filter = ('is_remote', 'is_conference', 'hide_from_press') + list_filter = ("is_remote", "is_conference", "hide_from_press") raw_id_fields = ( - 'carousel', - 'current_issue', - 'thumbnail_image', - 'xsl', + "carousel", + "current_issue", + "thumbnail_image", + "xsl", ) - filter_horizontal = ('keywords',) + filter_horizontal = ("keywords",) class PinnedArticleAdmin(admin.ModelAdmin): - list_display = ('article', 'journal', 'sequence') - list_filter = ('journal',) - search_fields = ('journal__code', 'article__title') - raw_id_fields = ('article',) + list_display = ("article", "journal", "sequence") + list_filter = ("journal",) + search_fields = ("journal__code", "article__title") + raw_id_fields = ("article",) class BannedIPAdmin(admin.ModelAdmin): - list_display = ('ip', 'date_banned') - list_filter = ('date_banned',) - search_fields = ('ip', ) - date_hierarchy = ('date_banned') + list_display = ("ip", "date_banned") + list_filter = ("date_banned",) + search_fields = ("ip",) + date_hierarchy = "date_banned" class NotificationsAdmin(admin.ModelAdmin): - list_display = ('journal', 'user', 'domain', 'type', 'active') - list_filter = ('journal', 'domain', 'type', 'active') - search_fields = ('journal__code', 'user__email', - 'user__first_name', 'user__last_name', - 'domain', 'type') - raw_id_fields = ('user',) + list_display = ("journal", "user", "domain", "type", "active") + list_filter = ("journal", "domain", "type", "active") + search_fields = ( + "journal__code", + "user__email", + "user__first_name", + "user__last_name", + "domain", + "type", + ) + raw_id_fields = ("user",) class ArticleOrderingAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('order', '_article', '_issue', '_section', '_journal') - list_filter = ('article__journal',) - search_fields = ('article__title', 'section__name', 'issue__issue_title', - 'issue__journal__code', 'issue__volume', 'issue__issue') - raw_id_fields = ('article',) + list_display = ("order", "_article", "_issue", "_section", "_journal") + list_filter = ("article__journal",) + search_fields = ( + "article__title", + "section__name", + "issue__issue_title", + "issue__journal__code", + "issue__volume", + "issue__issue", + ) + raw_id_fields = ("article",) def _issue(self, obj): return truncatewords(obj.issue.__str__(), 10) @@ -118,47 +149,70 @@ def _section(self, obj): class FixedPubCheckItemsAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_article', '_journal', 'metadata', 'verify_doi', - 'select_issue', 'set_pub_date', 'send_notifications', - 'select_render_galley', 'select_article_image', - 'select_open_reviews') - list_filter = ('article__journal', 'metadata', 'verify_doi', - 'select_issue', 'set_pub_date', 'send_notifications', - 'select_render_galley', 'select_article_image', - 'select_open_reviews') - search_fields = ('article__pk', 'article__title', 'article__journal__code') - raw_id_fields = ('article',) + list_display = ( + "_article", + "_journal", + "metadata", + "verify_doi", + "select_issue", + "set_pub_date", + "send_notifications", + "select_render_galley", + "select_article_image", + "select_open_reviews", + ) + list_filter = ( + "article__journal", + "metadata", + "verify_doi", + "select_issue", + "set_pub_date", + "send_notifications", + "select_render_galley", + "select_article_image", + "select_open_reviews", + ) + search_fields = ("article__pk", "article__title", "article__journal__code") + raw_id_fields = ("article",) class PresetPublicationCheckItemAdmin(admin.ModelAdmin): - list_display = ('journal', 'title', 'enabled') - list_filter = ('journal', 'enabled') - search_fields = ('journal__code', 'title', 'text') + list_display = ("journal", "title", "enabled") + list_filter = ("journal", "enabled") + search_fields = ("journal__code", "title", "text") class PrePublicationChecklistItemAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_article', '_journal', 'completed', 'completed_by', - 'completed_on') - list_filter = ('article__journal', 'completed', 'completed_on') - search_fields = ('article__title', 'article__journal__code', - 'completed_by__email', 'completed_by__first_name', - 'completed_by__last_name') - date_hierarchy = ('completed_on') + list_display = ("_article", "_journal", "completed", "completed_by", "completed_on") + list_filter = ("article__journal", "completed", "completed_on") + search_fields = ( + "article__title", + "article__journal__code", + "completed_by__email", + "completed_by__first_name", + "completed_by__last_name", + ) + date_hierarchy = "completed_on" raw_id_fields = ( - 'completed_by', - 'article', + "completed_by", + "article", ) class SectionOrderingAdmin(admin.ModelAdmin): - list_display = ('pk', '_section', '_issue', '_journal', 'order') - list_display_links = ('_section',) - list_filter = ('issue__journal',) - search_fields = ('section__name', 'issue__issue_title', - 'issue__journal__code', 'issue__volume', 'issue__issue') + list_display = ("pk", "_section", "_issue", "_journal", "order") + list_display_links = ("_section",) + list_filter = ("issue__journal",) + search_fields = ( + "section__name", + "issue__issue_title", + "issue__journal__code", + "issue__volume", + "issue__issue", + ) def _journal(self, obj): - return obj.issue.journal if obj else '' + return obj.issue.journal if obj else "" def _section(self, obj): return truncatewords(obj.section.__str__(), 10) diff --git a/src/journal/apps.py b/src/journal/apps.py index f7b358f0b5..31ce8401cd 100755 --- a/src/journal/apps.py +++ b/src/journal/apps.py @@ -8,4 +8,4 @@ class JournalConfig(AppConfig): - name = 'journal' + name = "journal" diff --git a/src/journal/decorators.py b/src/journal/decorators.py index 15a4ba5e1d..61784ba6a1 100644 --- a/src/journal/decorators.py +++ b/src/journal/decorators.py @@ -15,11 +15,7 @@ def frontend_enabled(func): @wraps(func) def frontend_enabled_wrapper(request, *args, **kwargs): if request.journal and request.journal.disable_front_end: - return redirect( - reverse( - 'journal_submissions' - ) - ) + return redirect(reverse("journal_submissions")) return func(request, *args, **kwargs) return frontend_enabled_wrapper diff --git a/src/journal/forms.py b/src/journal/forms.py index 7c0cccf45a..d571b63969 100755 --- a/src/journal/forms.py +++ b/src/journal/forms.py @@ -17,59 +17,62 @@ from utils.forms import CaptchaForm SEARCH_SORT_OPTIONS = [ - # Translators: Search order options - ('relevance', _('Relevance')), - ('title', _('Titles A-Z')), - ('-title', _('Titles Z-A')), - ('-date_published', _('Newest')), - ('date_published', _('Oldest')), - ] + # Translators: Search order options + ("relevance", _("Relevance")), + ("title", _("Titles A-Z")), + ("-title", _("Titles Z-A")), + ("-date_published", _("Newest")), + ("date_published", _("Oldest")), +] class JournalForm(forms.ModelForm): - class Meta: model = journal_models.Journal - fields = ('code', 'domain') + fields = ("code", "domain") help_texts = { - 'domain': 'Using a custom domain requires configuring DNS. ' - 'The journal will always be available under the /code path', + "domain": "Using a custom domain requires configuring DNS. " + "The journal will always be available under the /code path", } class ContactForm(forms.ModelForm, CaptchaForm): - def __init__(self, *args, **kwargs): - subject = kwargs.pop('subject', None) - contacts = kwargs.pop('contacts', None) + subject = kwargs.pop("subject", None) + contacts = kwargs.pop("contacts", None) super(ContactForm, self).__init__(*args, **kwargs) if subject: - self.fields['subject'].initial = subject + self.fields["subject"].initial = subject if contacts: contact_choices = [] for contact in contacts: - contact_choices.append([contact.email, '{name}, {role}'.format(name=contact.name, role=contact.role)]) - self.fields['recipient'].widget = forms.Select(choices=contact_choices) + contact_choices.append( + [ + contact.email, + "{name}, {role}".format(name=contact.name, role=contact.role), + ] + ) + self.fields["recipient"].widget = forms.Select(choices=contact_choices) class Meta: model = core_models.Contact - fields = ('recipient', 'sender', 'subject', 'body') + fields = ("recipient", "sender", "subject", "body") class ResendEmailForm(forms.Form): - to = forms.CharField(max_length=1000, help_text='Seperate email addresses with ;') + to = forms.CharField(max_length=1000, help_text="Seperate email addresses with ;") subject = forms.CharField(max_length=1000) body = forms.CharField(widget=TinyMCE) def __init__(self, *args, **kwargs): - log_entry = kwargs.pop('log_entry') + log_entry = kwargs.pop("log_entry") super(ResendEmailForm, self).__init__(*args, **kwargs) - self.fields['to'].initial = ';'.join(log_entry.to) - self.fields['subject'].initial = log_entry.subject - self.fields['body'].initial = mark_safe(log_entry.description) + self.fields["to"].initial = ";".join(log_entry.to) + self.fields["subject"].initial = log_entry.subject + self.fields["body"].initial = mark_safe(log_entry.description) class SearchForm(forms.Form): @@ -87,25 +90,37 @@ def __init__(self, data=None, *args, **kwargs): data = {k: v for k, v in data.items()} super().__init__(data, *args, **kwargs) if not settings.ENABLE_FULL_TEXT_SEARCH: - self.fields.pop('full_text', None) + self.fields.pop("full_text", None) self.fields["sort"].choices = SEARCH_SORT_OPTIONS[1:] if self.data and not self.has_filter: for search_filter in self.SEARCH_FILTERS: self.data[search_filter] = "on" - self.label_suffix = '' - - article_search = forms.CharField(label=_('Search term'), min_length=3, max_length=100, required=False) - title = forms.BooleanField(initial=True, label=_('Search Titles'), required=False) - abstract = forms.BooleanField(initial=True, label=_('Search Abstract'), required=False) - authors = forms.BooleanField(initial=True, label=_('Search Authors'), required=False) - keywords = forms.BooleanField(initial=True, label=_("Search Keywords"), required=False) - full_text = forms.BooleanField(initial=True, label=_("Search Full Text"), required=False) + self.label_suffix = "" + + article_search = forms.CharField( + label=_("Search term"), min_length=3, max_length=100, required=False + ) + title = forms.BooleanField(initial=True, label=_("Search Titles"), required=False) + abstract = forms.BooleanField( + initial=True, label=_("Search Abstract"), required=False + ) + authors = forms.BooleanField( + initial=True, label=_("Search Authors"), required=False + ) + keywords = forms.BooleanField( + initial=True, label=_("Search Keywords"), required=False + ) + full_text = forms.BooleanField( + initial=True, label=_("Search Full Text"), required=False + ) orcid = forms.BooleanField(label=_("Search ORCIDs"), required=False) - sort = forms.ChoiceField(label=_('Sort results by'), widget=forms.Select, choices=SEARCH_SORT_OPTIONS) + sort = forms.ChoiceField( + label=_("Sort results by"), widget=forms.Select, choices=SEARCH_SORT_OPTIONS + ) def get_search_filters(self): - """ Generates a dictionary of search_filters from a search form""" + """Generates a dictionary of search_filters from a search form""" return { "full_text": self.cleaned_data["full_text"], "title": self.cleaned_data["title"], @@ -115,7 +130,6 @@ def get_search_filters(self): "orcid": self.cleaned_data["orcid"], } - @cached_property def has_filter(self): """Determines if the user has selected at least one search filter @@ -128,25 +142,24 @@ class IssueDisplayForm(forms.ModelForm): class Meta: model = journal_models.Journal fields = ( - 'display_issue_volume', - 'display_issue_number', - 'display_issue_year', - 'display_issue_title', - 'display_article_number', - 'display_article_page_numbers', - 'display_issue_doi', - 'display_issues_grouped_by_decade', + "display_issue_volume", + "display_issue_number", + "display_issue_year", + "display_issue_title", + "display_article_number", + "display_article_page_numbers", + "display_issue_doi", + "display_issues_grouped_by_decade", ) class BasePrepubNotificationFormSet(forms.BaseFormSet): - def get_form_kwargs(self, index): kwargs = super().get_form_kwargs(index) if index == 0: - kwargs['setting_name'] = 'author_publication' + kwargs["setting_name"] = "author_publication" elif index == 1: - kwargs['setting_name'] = 'peer_reviewer_pub_notification' + kwargs["setting_name"] = "peer_reviewer_pub_notification" return kwargs diff --git a/src/journal/issue_forms.py b/src/journal/issue_forms.py index 8b0ed04b45..15f6a1e6ea 100755 --- a/src/journal/issue_forms.py +++ b/src/journal/issue_forms.py @@ -10,12 +10,12 @@ class NewIssue(forms.ModelForm): - def __init__(self, *args, **kwargs): journal = kwargs.pop("journal") super().__init__(*args, **kwargs) self.fields["issue_type"].queryset = models.IssueType.objects.filter( - journal=journal) + journal=journal + ) if self.instance and self.instance.code: path = reverse( "journal_collection_by_code_with_digits", @@ -27,9 +27,18 @@ def __init__(self, *args, **kwargs): class Meta: model = models.Issue fields = ( - 'issue_title', 'volume', 'issue', 'date', 'issue_description', - 'short_description', 'cover_image', 'large_image', 'issue_type', - 'code', 'doi', 'isbn', + "issue_title", + "volume", + "issue", + "date", + "issue_description", + "short_description", + "cover_image", + "large_image", + "issue_type", + "code", + "doi", + "isbn", ) @@ -43,16 +52,16 @@ def __init__(self, *args, **kwargs): class SortForm(forms.Form): sort_field = forms.ChoiceField( choices=( - ('first_page', 'First Page'), - ('date_published', 'Date Published'), - ('title', 'Title, Alphabetically'), - ('article_number', 'Article Number'), - ('page_numbers', 'Page Numbers (Custom)'), + ("first_page", "First Page"), + ("date_published", "Date Published"), + ("title", "Title, Alphabetically"), + ("article_number", "Article Number"), + ("page_numbers", "Page Numbers (Custom)"), ) ) order = forms.ChoiceField( choices=( - ('dsc', 'Descending'), - ('asc', 'Ascending'), + ("dsc", "Descending"), + ("asc", "Ascending"), ) ) diff --git a/src/journal/logic.py b/src/journal/logic.py index 2c3d2bef54..e793c8e672 100755 --- a/src/journal/logic.py +++ b/src/journal/logic.py @@ -37,7 +37,7 @@ def install_cover(journal, request): - """ Installs the default cover for the journal (stored in Files/journal//cover.png) + """Installs the default cover for the journal (stored in Files/journal//cover.png) :param journal: the journal object :param request: the current request or None @@ -51,7 +51,7 @@ def install_cover(journal, request): uuid_filename="cover.png", label="Journal logo", description="Logo for the journal", - owner=owner + owner=owner, ) thumbnail_file.save() @@ -61,18 +61,16 @@ def install_cover(journal, request): def list_scss(journal): - """ Lists the SCSS override files for a journal + """Lists the SCSS override files for a journal :param journal: the journal in question :return: a list of SCSS files """ - scss_path = join( - settings.BASE_DIR, 'files', 'styling', 'journals', str(journal.id)) + scss_path = join(settings.BASE_DIR, "files", "styling", "journals", str(journal.id)) try: makedirs(scss_path, exist_ok=True) file_paths = [ - join(scss_path, f) - for f in listdir(scss_path) if isfile(join(scss_path, f)) + join(scss_path, f) for f in listdir(scss_path) if isfile(join(scss_path, f)) ] except FileNotFoundError: logger.warning("Failed to load scss from %s" % scss_path) @@ -88,9 +86,11 @@ def create_galley_from_file(file_object, article_object, owner=None): warnings.warn( "'create_galley_from_file' is deprecated and will be removed," " use production.logic.save_galley instead." - ) + ) new_filename = str(uuid4()) + str(os.path.splitext(file_object.uuid_filename)[1]) - folder_structure = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article_object.id)) + folder_structure = os.path.join( + settings.BASE_DIR, "files", "articles", str(article_object.id) + ) old_path = os.path.join(folder_structure, str(file_object.uuid_filename)) new_path = os.path.join(folder_structure, str(new_filename)) @@ -105,7 +105,7 @@ def create_galley_from_file(file_object, article_object, owner=None): label=file_object.label, description=file_object.description, owner=owner, - is_galley=True + is_galley=True, ) new_file.save() @@ -146,7 +146,6 @@ def get_best_galley(article, galleys): except core_models.Galley.DoesNotExist: pass try: - image_galley = galleys.get( file__mime_type__in=files.IMAGE_MIMETYPES, public=True, @@ -171,14 +170,14 @@ def get_galley_content(article, galleys, recover=False): if galley: return galley.file_content(recover=recover) else: - return '' + return "" def get_doi_data(article): request = get_current_request() try: - doi = identifier_models.Identifier.objects.get(id_type='doi', article=article) - doi_url= doi.get_doi_url() + doi = identifier_models.Identifier.objects.get(id_type="doi", article=article) + doi_url = doi.get_doi_url() logger.info("Fetching %s.." % doi_url) r = requests.get(doi_url, timeout=settings.HTTP_TIMEOUT_SECONDS) return [r, doi] @@ -204,7 +203,7 @@ def handle_new_issue(request): new_issue.save() else: new_issue = None - return [form, 'issue', new_issue] + return [form, "issue", new_issue] def handle_assign_issue(request, article, issue): @@ -213,16 +212,16 @@ def handle_assign_issue(request, article, issue): messages.add_message( request, messages.WARNING, - _('Articles without a section cannot be added to an issue.'), + _("Articles without a section cannot be added to an issue."), ) elif issue not in article.journal.issues: messages.add_message( - request, messages.WARNING, 'Issue not in this journal’s issue list.') + request, messages.WARNING, "Issue not in this journal’s issue list." + ) else: issue.articles.add(article) issue.save() - messages.add_message( - request, messages.SUCCESS, 'Article assigned to issue.') + messages.add_message(request, messages.SUCCESS, "Article assigned to issue.") event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_ASSIGNED_TO_ISSUE, article=article, @@ -235,55 +234,59 @@ def handle_assign_issue(request, article, issue): def handle_unassign_issue(request, article, issues): try: - issue_to_unassign = journal_models.Issue.objects.get(pk=request.POST.get('unassign_issue', None)) + issue_to_unassign = journal_models.Issue.objects.get( + pk=request.POST.get("unassign_issue", None) + ) if issue_to_unassign in issues: issue_to_unassign.articles.remove(article) issue_to_unassign.save() - messages.add_message(request, messages.SUCCESS, 'Article unassigned from issue.') + messages.add_message( + request, messages.SUCCESS, "Article unassigned from issue." + ) else: - - messages.add_message(request, messages.WARNING, 'Issue not in this journal’s issue list.') + messages.add_message( + request, messages.WARNING, "Issue not in this journal’s issue list." + ) except journal_models.Issue.DoesNotExist: - messages.add_message(request, messages.WARNING, 'Issue does not exist.') + messages.add_message(request, messages.WARNING, "Issue does not exist.") def get_initial_for_prepub_notifications(request, article): author_initial = {} - author_initial['to'] = article.correspondence_author.email if article.correspondence_author else None + author_initial["to"] = ( + article.correspondence_author.email if article.correspondence_author else None + ) cc = [au.email for au in article.non_correspondence_authors()] notify_section_editors = request.journal.get_setting( - 'general', - 'notify_section_editors_of_publication', + "general", + "notify_section_editors_of_publication", ) if notify_section_editors: cc.extend([ed.email for ed in article.section_editors()]) - author_initial['cc'] = ','.join(cc) + author_initial["cc"] = ",".join(cc) notify_peer_reviewers = request.journal.get_setting( - 'general', - 'notify_peer_reviewers_of_publication', + "general", + "notify_peer_reviewers_of_publication", ) if not notify_peer_reviewers or not article.peer_reviewers(): return [author_initial] else: peer_reviewer_initial = {} - custom_reply_to = request.journal.get_setting( - 'general', - 'replyto_address' - ) - peer_reviewer_initial['to'] = custom_reply_to or request.user.email + custom_reply_to = request.journal.get_setting("general", "replyto_address") + peer_reviewer_initial["to"] = custom_reply_to or request.user.email reviewer_emails = article.peer_reviewers(emails=True, completed=True) - peer_reviewer_initial['bcc'] = ','.join(reviewer_emails) + peer_reviewer_initial["bcc"] = ",".join(reviewer_emails) return [author_initial, peer_reviewer_initial] def handle_prepub_notifications(request, article, formset): kwargs = { - 'request': request, - 'article': article, - 'formset': formset, + "request": request, + "article": article, + "formset": formset, } event_logic.Events.raise_event( @@ -293,35 +296,30 @@ def handle_prepub_notifications(request, article, formset): ) article.fixedpubcheckitems.send_notifications = True article.fixedpubcheckitems.save() - messages.add_message( - request, - messages.SUCCESS, - 'Notifications sent.' - ) + messages.add_message(request, messages.SUCCESS, "Notifications sent.") def notify_author(request, article): - """ Note: This function is deprecated. Use handle_prepub_notifications instead. - """ + """Note: This function is deprecated. Use handle_prepub_notifications instead.""" kwargs = { - 'request': request, - 'article': article, - 'user_message': request.POST.get('email_to_author', 'No message from Editor.'), - 'section_editors': request.POST.get('section_editors', False), - 'peer_reviewers': request.POST.get('peer_reviewers', False), + "request": request, + "article": article, + "user_message": request.POST.get("email_to_author", "No message from Editor."), + "section_editors": request.POST.get("section_editors", False), + "peer_reviewers": request.POST.get("peer_reviewers", False), } - event_logic.Events.raise_event(event_logic.Events.ON_AUTHOR_PUBLICATION, - task_object=article, - **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_AUTHOR_PUBLICATION, task_object=article, **kwargs + ) article.fixedpubcheckitems.notify_the_author = True article.fixedpubcheckitems.save() - messages.add_message(request, messages.INFO, 'Author notified.') + messages.add_message(request, messages.INFO, "Author notified.") def set_render_galley(request, article): - galley_id = request.POST.get('render_galley') + galley_id = request.POST.get("render_galley") if galley_id: galley = core_models.Galley.objects.get(pk=galley_id) @@ -330,9 +328,9 @@ def set_render_galley(request, article): article.fixedpubcheckitems.save() article.save() - messages.add_message(request, messages.SUCCESS, 'Render galley has been set.') + messages.add_message(request, messages.SUCCESS, "Render galley has been set.") else: - messages.add_message(request, messages.WARNING, 'No galley id supplied.') + messages.add_message(request, messages.WARNING, "No galley id supplied.") def set_open_reviews(request, article): @@ -340,54 +338,62 @@ def set_open_reviews(request, article): reviews = article.completed_reviews_with_permission for review in reviews: - review.display_public = bool(request.POST.get('open-review-' + str(review.pk), False)) + review.display_public = bool( + request.POST.get("open-review-" + str(review.pk), False) + ) review.save() def set_article_image(request, article): from core import logic as core_logic - if 'delete_image' in request.POST: - delete_id = request.POST.get('delete_image') - file_to_delete = get_object_or_404(core_models.File, pk=delete_id, article_id=article.pk) + if "delete_image" in request.POST: + delete_id = request.POST.get("delete_image") + file_to_delete = get_object_or_404( + core_models.File, pk=delete_id, article_id=article.pk + ) - if file_to_delete == article.large_image_file and request.user.is_staff or request.user == file_to_delete.owner: + if ( + file_to_delete == article.large_image_file + and request.user.is_staff + or request.user == file_to_delete.owner + ): file_to_delete.delete() article.fixedpubcheckitems.select_article_image = False article.fixedpubcheckitems.save() if request.POST and request.FILES: - uploaded_file = request.FILES.get('image_file') + uploaded_file = request.FILES.get("image_file") if not article.large_image_file: new_file = files.save_file_to_article(uploaded_file, article, request.user) - new_file.label = 'Banner image' - new_file.description = 'Banner image' - new_file.privacy = 'public' + new_file.label = "Banner image" + new_file.description = "Banner image" + new_file.privacy = "public" new_file.save() article.large_image_file = new_file article.save() - messages.add_message(request, messages.SUCCESS, 'New file loaded') + messages.add_message(request, messages.SUCCESS, "New file loaded") else: new_file = files.overwrite_file( - uploaded_file, - article.large_image_file, - ('articles', article.pk), + uploaded_file, + article.large_image_file, + ("articles", article.pk), ) article.large_image_file = new_file article.save() - messages.add_message(request, messages.SUCCESS, 'File overwritten.') + messages.add_message(request, messages.SUCCESS, "File overwritten.") article.fixedpubcheckitems.select_article_image = True article.fixedpubcheckitems.save() - core_logic.resize_and_crop(new_file.self_article_path(), [750, 324], 'middle') + core_logic.resize_and_crop(new_file.self_article_path(), [750, 324], "middle") def send_contact_message(new_contact, request): - body = new_contact.body.replace('\n', '
') + body = new_contact.body.replace("\n", "
") message = """

This message is from {0}'s contact form.


@@ -396,8 +402,13 @@ def send_contact_message(new_contact, request):

Subject: {3}

Body:

{4}

- """.format(request.journal if request.journal else request.press, new_contact.sender, new_contact.recipient, - new_contact.subject, body) + """.format( + request.journal if request.journal else request.press, + new_contact.sender, + new_contact.recipient, + new_contact.subject, + body, + ) notify_email.send_email( new_contact.subject, @@ -411,46 +422,57 @@ def send_contact_message(new_contact, request): def handle_article_controls(request, sections): if request.POST: - page = request.GET.get('page', 1) - filters = request.POST.getlist('filter[]') - show = int(request.POST.get('show', 10)) - sort = request.POST.get('sort', '-date_published') + page = request.GET.get("page", 1) + filters = request.POST.getlist("filter[]") + show = int(request.POST.get("show", 10)) + sort = request.POST.get("sort", "-date_published") filters = [int(filter) for filter in filters] - return page, show, filters, sort, set_article_session_variables(request, page, filters, show, sort), True + return ( + page, + show, + filters, + sort, + set_article_session_variables(request, page, filters, show, sort), + True, + ) else: - page = request.GET.get('page', 1) - filters = request.session.get('article_filters', [section.pk for section in sections]) - show = request.session.get('article_show', 10) - sort = request.session.get('article_sort', '-date_published') - active_filters = request.session.get('active_filters', False) + page = request.GET.get("page", 1) + filters = request.session.get( + "article_filters", [section.pk for section in sections] + ) + show = request.session.get("article_show", 10) + sort = request.session.get("article_sort", "-date_published") + active_filters = request.session.get("active_filters", False) return page, show, filters, sort, None, active_filters def set_article_session_variables(request, page, filters, show, sort): - request.session['article_filters'] = filters - request.session['article_show'] = show - request.session['article_sort'] = sort - request.session['active_filters'] = True + request.session["article_filters"] = filters + request.session["article_show"] = show + request.session["article_sort"] = sort + request.session["active_filters"] = True - return redirect("{0}?page={1}".format(reverse('journal_articles'), page)) + return redirect("{0}?page={1}".format(reverse("journal_articles"), page)) def unset_article_session_variables(request): - del request.session['article_filters'] - del request.session['article_show'] - del request.session['article_sort'] - del request.session['active_filters'] + del request.session["article_filters"] + del request.session["article_show"] + del request.session["article_sort"] + del request.session["active_filters"] request.session.modified = True - page = request.GET.get('page', 1) + page = request.GET.get("page", 1) - return redirect("{0}?page={1}".format(reverse('journal_articles'), page)) + return redirect("{0}?page={1}".format(reverse("journal_articles"), page)) -def handle_search_controls(request, search_term=None, keyword=None, redir=False, sort='title'): +def handle_search_controls( + request, search_term=None, keyword=None, redir=False, sort="title" +): """Takes in request and handles post and get and handles for search :param request: required Request object :param search_term: None or incoming st @@ -460,34 +482,39 @@ def handle_search_controls(request, search_term=None, keyword=None, redir=False, :return: strings: search_term, keyword, sort, and redirect() or None. """ if request.POST: - form = SearchForm(request.POST) if form.is_valid(): - search_term = form.cleaned_data['article_search'] - sort = form.cleaned_data['sort'] + search_term = form.cleaned_data["article_search"] + sort = form.cleaned_data["sort"] if search_term: - form = SearchForm({'article_search':search_term, 'sort':sort}) + form = SearchForm({"article_search": search_term, "sort": sort}) else: # must get keyword from the GET request. there is no way to POST a keyword in current implementation. - keyword = request.GET.get('keyword', False) - form = SearchForm({'article_search':'', 'sort':sort}) - return search_term, keyword, sort, form, set_search_GET_variables(search_term, keyword, sort) + keyword = request.GET.get("keyword", False) + form = SearchForm({"article_search": "", "sort": sort}) + return ( + search_term, + keyword, + sort, + form, + set_search_GET_variables(search_term, keyword, sort), + ) # if form not valid no redir to send form w/errors else: return search_term, keyword, sort, form, redir else: - search_term = request.GET.get('article_search', '') - keyword = request.GET.get('keyword', False) - sort = request.GET.get('sort', 'title') + search_term = request.GET.get("article_search", "") + keyword = request.GET.get("keyword", False) + sort = request.GET.get("sort", "title") if sort == "relevance": - sort = 'title' + sort = "title" form = SearchForm(request.GET or None) return search_term, keyword, sort, form, None -def set_search_GET_variables(search_term=False, keyword=False, sort='title'): +def set_search_GET_variables(search_term=False, keyword=False, sort="title"): """Sets the incoming variables to be GET params and returns redirect :param search_term: string or false :param keyword: string or false @@ -495,52 +522,53 @@ def set_search_GET_variables(search_term=False, keyword=False, sort='title'): :return: redirect() """ if search_term: - get_params = urlencode({'article_search' : search_term, 'sort' : sort}) - redir_str = '{0}?{1}'.format(reverse('search'), get_params) + get_params = urlencode({"article_search": search_term, "sort": sort}) + redir_str = "{0}?{1}".format(reverse("search"), get_params) elif keyword: - get_params = urlencode({'keyword' : keyword, 'sort' : sort}) - redir_str = '{0}?{1}'.format(reverse('search'), get_params) + get_params = urlencode({"keyword": keyword, "sort": sort}) + redir_str = "{0}?{1}".format(reverse("search"), get_params) else: - redir_str = reverse('search') + redir_str = reverse("search") return redirect(redir_str) def fire_submission_notifications(**kwargs): - request = kwargs.get('request') + request = kwargs.get("request") - active_notifications = journal_models.Notifications.objects.filter(journal=request.journal, - active=True, - type='submission') - handle_notification(active_notifications, 'submission', **kwargs) + active_notifications = journal_models.Notifications.objects.filter( + journal=request.journal, active=True, type="submission" + ) + handle_notification(active_notifications, "submission", **kwargs) def fire_acceptance_notifications(**kwargs): - request = kwargs.get('request') - active_notifications = journal_models.Notifications.objects.filter(journal=request.journal, - active=True, - type='acceptance') - handle_notification(active_notifications, 'acceptance', **kwargs) + request = kwargs.get("request") + active_notifications = journal_models.Notifications.objects.filter( + journal=request.journal, active=True, type="acceptance" + ) + handle_notification(active_notifications, "acceptance", **kwargs) def handle_notification(notifications, type, **kwargs): - request = kwargs.pop('request') - article = kwargs.pop('article') - domain = article.correspondence_author.email.split('@')[1] + request = kwargs.pop("request") + article = kwargs.pop("article") + domain = article.correspondence_author.email.split("@")[1] for notification in notifications: if notification.domain == domain: - notify_helpers.send_email_with_body_from_setting_template(request, - 'notification_{0}'.format(type), - 'Article Notification', - notification.user.email, - {'article': article, - 'notification': notification}) + notify_helpers.send_email_with_body_from_setting_template( + request, + "notification_{0}".format(type), + "Article Notification", + notification.user.email, + {"article": article, "notification": notification}, + ) def create_html_snippet(note): - template = get_template('elements/notes/note_snippet.html') - html_content = template.render({'note': note}) + template = get_template("elements/notes/note_snippet.html") + html_content = template.render({"note": note}) return html_content @@ -557,28 +585,32 @@ def validate_to_list(to_list): def resend_email(article, log_entry, request, form): - to_list = [x.strip() for x in form.cleaned_data['to'].split(';') if x] + to_list = [x.strip() for x in form.cleaned_data["to"].split(";") if x] valid_email_addresses = validate_to_list(to_list) - subject = form.cleaned_data['subject'] - message = form.cleaned_data['body'] - log_dict = {'level': 'Info', - 'action_text': 'Resending an email.', - 'types': 'Email Resend', - 'target': article} + subject = form.cleaned_data["subject"] + message = form.cleaned_data["body"] + log_dict = { + "level": "Info", + "action_text": "Resending an email.", + "types": "Email Resend", + "target": article, + } - notify_helpers.send_email_with_body_from_user(request, subject, valid_email_addresses, message, log_dict=log_dict) + notify_helpers.send_email_with_body_from_user( + request, subject, valid_email_addresses, message, log_dict=log_dict + ) def send_email(user, form, request, article): - subject = form.cleaned_data['subject'] - message = form.cleaned_data['body'] + subject = form.cleaned_data["subject"] + message = form.cleaned_data["body"] log_dict = { - 'level': 'Info', - 'action_text': 'Contact User', - 'types': 'Email', - 'target': article if article else user + "level": "Info", + "action_text": "Contact User", + "types": "Email", + "target": article if article else user, } notify_helpers.send_email_with_body_from_user( @@ -587,9 +619,9 @@ def send_email(user, form, request, article): user.email, message, log_dict=log_dict, - cc=form.cleaned_data['cc'], - bcc=form.cleaned_data['bcc'], - attachment=form.cleaned_data['attachments'], + cc=form.cleaned_data["cc"], + bcc=form.cleaned_data["bcc"], + attachment=form.cleaned_data["attachments"], ) @@ -601,8 +633,8 @@ def get_table_from_html(table_name, content): :return: A table object """ - soup = BeautifulSoup(str(content), 'lxml') - table_div = soup.find("div", {'id': table_name}) + soup = BeautifulSoup(str(content), "lxml") + table_div = soup.find("div", {"id": table_name}) table = table_div.find("table") return table @@ -612,10 +644,10 @@ def get_all_tables_from_html(content): Uses BS4 to fetch all tables in html. :param content: HTML content """ - soup = BeautifulSoup(str(content), 'lxml') + soup = BeautifulSoup(str(content), "lxml") tables = [] - for table in soup.findAll('div', attrs={'class': 'table-expansion'}): + for table in soup.findAll("div", attrs={"class": "table-expansion"}): original_id = table.get("id") if original_id: table["id"] = "copy-of-" + original_id @@ -626,36 +658,37 @@ def get_all_tables_from_html(content): child["id"] = "copy-of-" + child["id"] except AttributeError: pass - tables.append( - { - 'id': original_id, - 'content': str(table) - } - ) + tables.append({"id": original_id, "content": str(table)}) return tables def parse_html_table_to_csv(table, table_name): - filepath = files.get_temp_file_path_from_name('{0}.csv'.format(table_name)) + filepath = files.get_temp_file_path_from_name("{0}.csv".format(table_name)) headers = [th.text for th in table.select("tr th")] with open(filepath, "w", encoding="utf-8") as f: wr = csv.writer(f) wr.writerow(headers) - wr.writerows([[td.text for td in row.find_all("td")] for row in table.select("tr + tr")]) + wr.writerows( + [[td.text for td in row.find_all("td")] for row in table.select("tr + tr")] + ) return filepath def potential_issue_editors(journal, current_editors): - return {role.user for role in - core_models.AccountRole.objects.filter( - journal=journal, - user__is_active=True, - ).select_related('user').exclude( - user__in=current_editors, - )} + return { + role.user + for role in core_models.AccountRole.objects.filter( + journal=journal, + user__is_active=True, + ) + .select_related("user") + .exclude( + user__in=current_editors, + ) + } def sort_issues(request, issue_list): @@ -665,21 +698,21 @@ def sort_issues(request, issue_list): :param issue_list: Issue queryset for sorting :return: None """ - sort_type = request.POST.get('sort', None) + sort_type = request.POST.get("sort", None) if not sort_type: messages.add_message( request, messages.WARNING, - 'No sort type provided.', + "No sort type provided.", ) return - if sort_type == 'date_sort_desc': - order = '-date' + if sort_type == "date_sort_desc": + order = "-date" else: - order = 'date' + order = "date" ordered_issues = issue_list.order_by(order) @@ -689,7 +722,7 @@ def sort_issues(request, issue_list): def merge_issues(destination, to_merge): - """ Moves the articles from to_merge issues into the destination issue + """Moves the articles from to_merge issues into the destination issue :param destination: models.Issue :param destination: list(models.Issue): """ @@ -705,7 +738,7 @@ def merge_issues(destination, to_merge): def merge_sections(destination, to_merge): - """ Moves the articles from to_merge sections into the destination section + """Moves the articles from to_merge sections into the destination section :param destination: submission.models.Section :param destination: list(submission.models.Section): """ diff --git a/src/journal/management/commands/galley_healthcheck.py b/src/journal/management/commands/galley_healthcheck.py index 5af339f380..c30c6b0154 100644 --- a/src/journal/management/commands/galley_healthcheck.py +++ b/src/journal/management/commands/galley_healthcheck.py @@ -14,10 +14,10 @@ class Command(BaseCommand): help = "Checks the health of all the published article galleys" def add_arguments(self, parser): - parser.add_argument('journal_codes', nargs='*', default=None) + parser.add_argument("journal_codes", nargs="*", default=None) def handle(self, *args, **options): - """ Healthchecks of all article galleys in two ways + """Healthchecks of all article galleys in two ways 1. Verifies all published articles have a PDF or a render galley 2. Verifies that all the images are available for each render galley @@ -25,7 +25,7 @@ def handle(self, *args, **options): :param options: None :return: None """ - journal_codes = options.get('journal_codes') + journal_codes = options.get("journal_codes") journals = journal_models.Journal.objects.all() if journal_codes: @@ -33,10 +33,13 @@ def handle(self, *args, **options): for journal in journals: articles = models.Article.objects.filter( - stage=models.STAGE_PUBLISHED, journal=journal) + stage=models.STAGE_PUBLISHED, journal=journal + ) for article in articles: - print("Verifying {article.pk} - {article.title}".format(article=article)) + print( + "Verifying {article.pk} - {article.title}".format(article=article) + ) render_galley = article.get_render_galley has_pdf = article.pdfs.exists() if not (has_pdf or render_galley): @@ -52,12 +55,9 @@ def handle(self, *args, **options): def retrieve_image_urls_from_galley(galley): xml_file_contents = galley.file.get_file(galley.article) - souped_xml = BeautifulSoup(xml_file_contents, 'lxml') + souped_xml = BeautifulSoup(xml_file_contents, "lxml") - elements = { - 'img': 'src', - 'graphic': 'xlink:href' - } + elements = {"img": "src", "graphic": "xlink:href"} return [ val.get(attribute) diff --git a/src/journal/management/commands/merge_issues.py b/src/journal/management/commands/merge_issues.py index 71fe05939b..30be37cfae 100755 --- a/src/journal/management/commands/merge_issues.py +++ b/src/journal/management/commands/merge_issues.py @@ -5,16 +5,18 @@ class Command(BaseCommand): - """ Merges the articles from a list of issues into the destination""" + """Merges the articles from a list of issues into the destination""" help = "Merges issues by moving the content from the issues to the " "destination issue and deletes the merged issues" def add_arguments(self, parser): - parser.add_argument('destination_id') - parser.add_argument('-i', '--issue-ids', - nargs='+', - ) + parser.add_argument("destination_id") + parser.add_argument( + "-i", + "--issue-ids", + nargs="+", + ) def handle(self, *args, **options): issue_ids = options["issue_ids"] @@ -25,5 +27,3 @@ def handle(self, *args, **options): issues = models.Issue.objects.filter(pk__in=issue_ids) destination = models.Issue.objects.get(pk=destination_id) merge_issues(destination, issues) - - diff --git a/src/journal/management/commands/merge_sections.py b/src/journal/management/commands/merge_sections.py index dceb1b7342..62eea8bf84 100755 --- a/src/journal/management/commands/merge_sections.py +++ b/src/journal/management/commands/merge_sections.py @@ -5,16 +5,18 @@ class Command(BaseCommand): - """ Merges the articles from a list of issues into the destination""" + """Merges the articles from a list of issues into the destination""" help = "Merges issues by moving the content from the issues to the " "destination issue and deletes the merged issues" def add_arguments(self, parser): - parser.add_argument('destination_id') - parser.add_argument('-s', '--section-ids', - nargs='+', - ) + parser.add_argument("destination_id") + parser.add_argument( + "-s", + "--section-ids", + nargs="+", + ) def handle(self, *args, **options): section_ids = options["section_ids"] @@ -22,8 +24,6 @@ def handle(self, *args, **options): destination_id = options["destination_id"] if destination_id in section_ids: raise RuntimeError("Can't merge a section with itself") - sections = submission_models.Section.objects.filter( - pk__in=section_ids) - destination = submission_models.Section.objects.get( - pk=destination_id) + sections = submission_models.Section.objects.filter(pk__in=section_ids) + destination = submission_models.Section.objects.get(pk=destination_id) merge_sections(destination, sections) diff --git a/src/journal/middleware.py b/src/journal/middleware.py index 92acfb1332..e2d2c1cda4 100644 --- a/src/journal/middleware.py +++ b/src/journal/middleware.py @@ -21,18 +21,19 @@ def process_request(request): if request.journal and settings.USE_I18N: current_language = translation.get_language() available_languages = request.journal.get_setting( - group_name='general', - setting_name='journal_languages', + group_name="general", + setting_name="journal_languages", ) default_language = request.journal.get_setting( - group_name='general', - setting_name='default_journal_language' + group_name="general", setting_name="default_journal_language" ) if current_language not in available_languages: translation.activate(settings.LANGUAGE_CODE) - logger.debug('Current Language not in the available languages. Activating {}'.format( - settings.LANGUAGE_CODE, - )) + logger.debug( + "Current Language not in the available languages. Activating {}".format( + settings.LANGUAGE_CODE, + ) + ) if not available_languages: # If we have no languages use the defaults from settings. diff --git a/src/journal/migrations/0001_initial.py b/src/journal/migrations/0001_initial.py index 13bd293810..a5bd8e3347 100755 --- a/src/journal/migrations/0001_initial.py +++ b/src/journal/migrations/0001_initial.py @@ -10,119 +10,278 @@ class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='ArticleOrdering', + name="ArticleOrdering", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=1)), ], ), migrations.CreateModel( - name='BannedIPs', + name="BannedIPs", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ip', models.GenericIPAddressField()), - ('date_banned', models.DateField(auto_now_add=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("ip", models.GenericIPAddressField()), + ("date_banned", models.DateField(auto_now_add=True)), ], ), migrations.CreateModel( - name='FixedPubCheckItems', + name="FixedPubCheckItems", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('metadata', models.BooleanField(default=False)), - ('verify_doi', models.BooleanField(default=False)), - ('select_issue', models.BooleanField(default=False)), - ('set_pub_date', models.BooleanField(default=False)), - ('notify_the_author', models.BooleanField(default=False)), - ('select_render_galley', models.BooleanField(default=False)), - ('select_article_image', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("metadata", models.BooleanField(default=False)), + ("verify_doi", models.BooleanField(default=False)), + ("select_issue", models.BooleanField(default=False)), + ("set_pub_date", models.BooleanField(default=False)), + ("notify_the_author", models.BooleanField(default=False)), + ("select_render_galley", models.BooleanField(default=False)), + ("select_article_image", models.BooleanField(default=False)), ], ), migrations.CreateModel( - name='Issue', + name="Issue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('volume', models.IntegerField(default=1)), - ('issue', models.IntegerField(default=1)), - ('issue_title', models.CharField(blank=True, max_length=300)), - ('date', models.DateTimeField(default=django.utils.timezone.now)), - ('order', models.IntegerField(default=1)), - ('issue_type', models.CharField(choices=[('Issue', 'Issue'), ('Collection', 'Collection')], default='Issue', max_length=200)), - ('issue_description', models.TextField()), - ('cover_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path)), - ('large_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.issue_large_image_path)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("volume", models.IntegerField(default=1)), + ("issue", models.IntegerField(default=1)), + ("issue_title", models.CharField(blank=True, max_length=300)), + ("date", models.DateTimeField(default=django.utils.timezone.now)), + ("order", models.IntegerField(default=1)), + ( + "issue_type", + models.CharField( + choices=[("Issue", "Issue"), ("Collection", "Collection")], + default="Issue", + max_length=200, + ), + ), + ("issue_description", models.TextField()), + ( + "cover_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), + ), + ( + "large_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.issue_large_image_path, + ), + ), ], options={ - 'ordering': ('order', '-date'), + "ordering": ("order", "-date"), }, ), migrations.CreateModel( - name='Journal', + name="Journal", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('code', models.CharField(max_length=4)), - ('domain', models.CharField(default='localhost', max_length=255, unique=True)), - ('default_cover_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path)), - ('default_large_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path)), - ('header_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path)), - ('favicon', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path)), - ('description', models.TextField(blank=True, null=True, verbose_name='Journal Description')), - ('is_remote', models.BooleanField(default=False)), - ('remote_submit_url', models.URLField(blank=True, null=True)), - ('remote_view_url', models.URLField(blank=True, null=True)), - ('nav_home', models.BooleanField(default=True)), - ('nav_articles', models.BooleanField(default=True)), - ('nav_issues', models.BooleanField(default=True)), - ('nav_contact', models.BooleanField(default=True)), - ('nav_start', models.BooleanField(default=True)), - ('nav_review', models.BooleanField(default=True)), - ('nav_sub', models.BooleanField(default=True)), - ('has_xslt', models.BooleanField(default=False)), - ('hide_from_press', models.BooleanField(default=False)), - ('sequence', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("code", models.CharField(max_length=4)), + ( + "domain", + models.CharField(default="localhost", max_length=255, unique=True), + ), + ( + "default_cover_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), + ), + ( + "default_large_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), + ), + ( + "header_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), + ), + ( + "favicon", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), + ), + ( + "description", + models.TextField( + blank=True, null=True, verbose_name="Journal Description" + ), + ), + ("is_remote", models.BooleanField(default=False)), + ("remote_submit_url", models.URLField(blank=True, null=True)), + ("remote_view_url", models.URLField(blank=True, null=True)), + ("nav_home", models.BooleanField(default=True)), + ("nav_articles", models.BooleanField(default=True)), + ("nav_issues", models.BooleanField(default=True)), + ("nav_contact", models.BooleanField(default=True)), + ("nav_start", models.BooleanField(default=True)), + ("nav_review", models.BooleanField(default=True)), + ("nav_sub", models.BooleanField(default=True)), + ("has_xslt", models.BooleanField(default=False)), + ("hide_from_press", models.BooleanField(default=False)), + ("sequence", models.PositiveIntegerField(default=0)), ], ), migrations.CreateModel( - name='Notifications', + name="Notifications", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('domain', models.CharField(max_length=100)), - ('type', models.CharField(choices=[('submission', 'Submission'), ('acceptance', 'Acceptance')], max_length=10)), - ('active', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("domain", models.CharField(max_length=100)), + ( + "type", + models.CharField( + choices=[ + ("submission", "Submission"), + ("acceptance", "Acceptance"), + ], + max_length=10, + ), + ), + ("active", models.BooleanField(default=False)), ], ), migrations.CreateModel( - name='PrePublicationChecklistItem', + name="PrePublicationChecklistItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('completed', models.BooleanField(default=False)), - ('completed_on', models.DateTimeField(blank=True, null=True)), - ('title', models.TextField()), - ('text', models.TextField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("completed", models.BooleanField(default=False)), + ("completed_on", models.DateTimeField(blank=True, null=True)), + ("title", models.TextField()), + ("text", models.TextField()), ], ), migrations.CreateModel( - name='PresetPublicationCheckItem', + name="PresetPublicationCheckItem", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.TextField()), - ('text', models.TextField()), - ('enabled', models.BooleanField(default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.TextField()), + ("text", models.TextField()), + ("enabled", models.BooleanField(default=True)), ], ), migrations.CreateModel( - name='SectionOrdering', + name="SectionOrdering", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=1)), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Issue')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=1)), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Issue" + ), + ), ], ), ] diff --git a/src/journal/migrations/0002_auto_20170711_1203.py b/src/journal/migrations/0002_auto_20170711_1203.py index 98de462d81..fdad0f1dd3 100755 --- a/src/journal/migrations/0002_auto_20170711_1203.py +++ b/src/journal/migrations/0002_auto_20170711_1203.py @@ -8,100 +8,155 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('journal', '0001_initial'), + ("journal", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0001_initial'), - ('submission', '0001_initial'), - ('carousel', '0002_auto_20170711_1203'), + ("core", "0001_initial"), + ("submission", "0001_initial"), + ("carousel", "0002_auto_20170711_1203"), ] operations = [ migrations.AddField( - model_name='sectionordering', - name='section', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Section'), - ), - migrations.AddField( - model_name='presetpublicationcheckitem', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), - migrations.AddField( - model_name='prepublicationchecklistitem', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), - ), - migrations.AddField( - model_name='prepublicationchecklistitem', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='notifications', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), - migrations.AddField( - model_name='notifications', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='journal', - name='carousel', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='journal', to='carousel.Carousel'), - ), - migrations.AddField( - model_name='journal', - name='current_issue', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_issue', to='journal.Issue'), - ), - migrations.AddField( - model_name='journal', - name='press_image_override', - field=models.ForeignKey(blank=True, help_text='Replaces the press logo in the footer.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='press_image_override', to='core.File'), - ), - migrations.AddField( - model_name='journal', - name='thumbnail_image', - field=models.ForeignKey(blank=True, help_text="The default thumbnail for articles, not to be confused with 'Default cover image'.", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='thumbnail_image', to='core.File'), - ), - migrations.AddField( - model_name='issue', - name='articles', - field=models.ManyToManyField(blank=True, null=True, related_name='issues', to='submission.Article'), - ), - migrations.AddField( - model_name='issue', - name='guest_editors', - field=models.ManyToManyField(blank=True, null=True, related_name='guest_editors', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='issue', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), - ), - migrations.AddField( - model_name='fixedpubcheckitems', - name='article', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), - ), - migrations.AddField( - model_name='articleordering', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), - ), - migrations.AddField( - model_name='articleordering', - name='issue', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Issue'), + model_name="sectionordering", + name="section", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Section" + ), + ), + migrations.AddField( + model_name="presetpublicationcheckitem", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="prepublicationchecklistitem", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), + ), + migrations.AddField( + model_name="prepublicationchecklistitem", + name="completed_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="notifications", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="notifications", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.AddField( + model_name="journal", + name="carousel", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="journal", + to="carousel.Carousel", + ), + ), + migrations.AddField( + model_name="journal", + name="current_issue", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="current_issue", + to="journal.Issue", + ), + ), + migrations.AddField( + model_name="journal", + name="press_image_override", + field=models.ForeignKey( + blank=True, + help_text="Replaces the press logo in the footer.", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="press_image_override", + to="core.File", + ), + ), + migrations.AddField( + model_name="journal", + name="thumbnail_image", + field=models.ForeignKey( + blank=True, + help_text="The default thumbnail for articles, not to be confused with 'Default cover image'.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="thumbnail_image", + to="core.File", + ), + ), + migrations.AddField( + model_name="issue", + name="articles", + field=models.ManyToManyField( + blank=True, null=True, related_name="issues", to="submission.Article" + ), + ), + migrations.AddField( + model_name="issue", + name="guest_editors", + field=models.ManyToManyField( + blank=True, + null=True, + related_name="guest_editors", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="issue", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), + ), + migrations.AddField( + model_name="fixedpubcheckitems", + name="article", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), + ), + migrations.AddField( + model_name="articleordering", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), + ), + migrations.AddField( + model_name="articleordering", + name="issue", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Issue" + ), ), migrations.AlterUniqueTogether( - name='articleordering', - unique_together=set([('article', 'issue')]), + name="articleordering", + unique_together=set([("article", "issue")]), ), ] diff --git a/src/journal/migrations/0003_pinnedarticle.py b/src/journal/migrations/0003_pinnedarticle.py index 6e10891524..fae39df61b 100755 --- a/src/journal/migrations/0003_pinnedarticle.py +++ b/src/journal/migrations/0003_pinnedarticle.py @@ -7,20 +7,39 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0001_initial'), - ('journal', '0002_auto_20170711_1203'), + ("submission", "0001_initial"), + ("journal", "0002_auto_20170711_1203"), ] operations = [ migrations.CreateModel( - name='PinnedArticle', + name="PinnedArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sequence', models.PositiveIntegerField(default=0)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("sequence", models.PositiveIntegerField(default=0)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), ] diff --git a/src/journal/migrations/0004_auto_20170813_1302.py b/src/journal/migrations/0004_auto_20170813_1302.py index 6c26fcf9f2..b272c48b8e 100755 --- a/src/journal/migrations/0004_auto_20170813_1302.py +++ b/src/journal/migrations/0004_auto_20170813_1302.py @@ -8,44 +8,85 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0003_pinnedarticle'), + ("journal", "0003_pinnedarticle"), ] operations = [ migrations.AlterModelOptions( - name='pinnedarticle', - options={'ordering': ('sequence',)}, + name="pinnedarticle", + options={"ordering": ("sequence",)}, ), migrations.AlterField( - model_name='issue', - name='cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="issue", + name="cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='issue', - name='large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.issue_large_image_path), + model_name="issue", + name="large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.issue_large_image_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='header_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="header_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0005_auto_20171002_1503.py b/src/journal/migrations/0005_auto_20171002_1503.py index 28ffcc7f43..958ead3510 100755 --- a/src/journal/migrations/0005_auto_20171002_1503.py +++ b/src/journal/migrations/0005_auto_20171002_1503.py @@ -8,45 +8,86 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0004_auto_20170813_1302'), + ("journal", "0004_auto_20170813_1302"), ] operations = [ migrations.AddField( - model_name='journal', - name='nav_news', + model_name="journal", + name="nav_news", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='issue', - name='cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="issue", + name="cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='issue', - name='large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.issue_large_image_path), + model_name="issue", + name="large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.issue_large_image_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='header_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="header_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0006_auto_20171115_1216.py b/src/journal/migrations/0006_auto_20171115_1216.py index ede5693d43..6beee7e11b 100755 --- a/src/journal/migrations/0006_auto_20171115_1216.py +++ b/src/journal/migrations/0006_auto_20171115_1216.py @@ -6,20 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0005_auto_20171002_1503'), + ("journal", "0005_auto_20171002_1503"), ] operations = [ migrations.AlterField( - model_name='journal', - name='code', + model_name="journal", + name="code", field=models.CharField(max_length=10), ), migrations.AlterField( - model_name='journal', - name='domain', - field=models.CharField(default='www.example.com', max_length=255, unique=True), + model_name="journal", + name="domain", + field=models.CharField( + default="www.example.com", max_length=255, unique=True + ), ), ] diff --git a/src/journal/migrations/0007_auto_20180129_2342.py b/src/journal/migrations/0007_auto_20180129_2342.py index f0e7d39ad3..271dfaa70a 100644 --- a/src/journal/migrations/0007_auto_20180129_2342.py +++ b/src/journal/migrations/0007_auto_20180129_2342.py @@ -7,26 +7,35 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), - ('journal', '0006_auto_20171115_1216'), + ("submission", "0019_auto_20171130_1115"), + ("journal", "0006_auto_20171115_1216"), ] operations = [ migrations.AddField( - model_name='articleordering', - name='section', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='submission.Section'), + model_name="articleordering", + name="section", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Section", + ), preserve_default=False, ), migrations.AlterField( - model_name='journal', - name='current_issue', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_issue', to='journal.Issue'), + model_name="journal", + name="current_issue", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="current_issue", + to="journal.Issue", + ), ), migrations.AlterUniqueTogether( - name='articleordering', - unique_together=set([('article',)]), + name="articleordering", + unique_together=set([("article",)]), ), ] diff --git a/src/journal/migrations/0007_auto_20180208_1225.py b/src/journal/migrations/0007_auto_20180208_1225.py index 1e9572a834..dd279bee60 100644 --- a/src/journal/migrations/0007_auto_20180208_1225.py +++ b/src/journal/migrations/0007_auto_20180208_1225.py @@ -7,15 +7,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0006_auto_20171115_1216'), + ("journal", "0006_auto_20171115_1216"), ] operations = [ migrations.AlterField( - model_name='journal', - name='current_issue', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_issue', to='journal.Issue'), + model_name="journal", + name="current_issue", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="current_issue", + to="journal.Issue", + ), ), ] diff --git a/src/journal/migrations/0008_auto_20180129_2352.py b/src/journal/migrations/0008_auto_20180129_2352.py index b4f45eede0..6e6b33071b 100644 --- a/src/journal/migrations/0008_auto_20180129_2352.py +++ b/src/journal/migrations/0008_auto_20180129_2352.py @@ -7,15 +7,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), - ('journal', '0007_auto_20180129_2342'), + ("submission", "0019_auto_20171130_1115"), + ("journal", "0007_auto_20180129_2342"), ] operations = [ migrations.AlterUniqueTogether( - name='articleordering', - unique_together=set([('article', 'issue', 'section')]), + name="articleordering", + unique_together=set([("article", "issue", "section")]), ), ] diff --git a/src/journal/migrations/0009_merge_20180208_1443.py b/src/journal/migrations/0009_merge_20180208_1443.py index 51e4fe1a4d..920d5e063e 100644 --- a/src/journal/migrations/0009_merge_20180208_1443.py +++ b/src/journal/migrations/0009_merge_20180208_1443.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0007_auto_20180208_1225'), - ('journal', '0008_auto_20180129_2352'), + ("journal", "0007_auto_20180208_1225"), + ("journal", "0008_auto_20180129_2352"), ] - operations = [ - ] + operations = [] diff --git a/src/journal/migrations/0010_auto_20180412_1544.py b/src/journal/migrations/0010_auto_20180412_1544.py index 0cf59f2e8c..a072c7df5a 100644 --- a/src/journal/migrations/0010_auto_20180412_1544.py +++ b/src/journal/migrations/0010_auto_20180412_1544.py @@ -6,22 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0009_merge_20180208_1443'), + ("journal", "0009_merge_20180208_1443"), ] operations = [ migrations.AlterModelOptions( - name='articleordering', - options={'ordering': ('order', 'section')}, + name="articleordering", + options={"ordering": ("order", "section")}, ), migrations.AlterModelOptions( - name='bannedips', - options={'verbose_name_plural': 'Banned IPs'}, + name="bannedips", + options={"verbose_name_plural": "Banned IPs"}, ), migrations.AlterModelOptions( - name='sectionordering', - options={'ordering': ('order', 'section')}, + name="sectionordering", + options={"ordering": ("order", "section")}, ), ] diff --git a/src/journal/migrations/0010_auto_20180709_1207.py b/src/journal/migrations/0010_auto_20180709_1207.py index 4c5e12e3d6..5069b37a51 100644 --- a/src/journal/migrations/0010_auto_20180709_1207.py +++ b/src/journal/migrations/0010_auto_20180709_1207.py @@ -6,23 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0009_merge_20180208_1443'), + ("journal", "0009_merge_20180208_1443"), ] operations = [ migrations.AlterModelOptions( - name='articleordering', - options={'ordering': ('order', 'section')}, + name="articleordering", + options={"ordering": ("order", "section")}, ), migrations.AlterModelOptions( - name='sectionordering', - options={'ordering': ('order', 'section')}, + name="sectionordering", + options={"ordering": ("order", "section")}, ), migrations.AddField( - model_name='journal', - name='contact_info', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), ] diff --git a/src/journal/migrations/0011_merge_20180716_1312.py b/src/journal/migrations/0011_merge_20180716_1312.py index da0a0cab81..f81c828ca6 100644 --- a/src/journal/migrations/0011_merge_20180716_1312.py +++ b/src/journal/migrations/0011_merge_20180716_1312.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0010_auto_20180709_1207'), - ('journal', '0010_auto_20180412_1544'), + ("journal", "0010_auto_20180709_1207"), + ("journal", "0010_auto_20180412_1544"), ] - operations = [ - ] + operations = [] diff --git a/src/journal/migrations/0012_auto_20180824_1025.py b/src/journal/migrations/0012_auto_20180824_1025.py index 017b2f6632..c92d4430c9 100644 --- a/src/journal/migrations/0012_auto_20180824_1025.py +++ b/src/journal/migrations/0012_auto_20180824_1025.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0011_merge_20180716_1312'), + ("journal", "0011_merge_20180716_1312"), ] operations = [ migrations.AlterField( - model_name='issue', - name='issue_description', + model_name="issue", + name="issue_description", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/journal/migrations/0013_journal_nav_collections.py b/src/journal/migrations/0013_journal_nav_collections.py index 5298d7c692..cccae5929f 100644 --- a/src/journal/migrations/0013_journal_nav_collections.py +++ b/src/journal/migrations/0013_journal_nav_collections.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0012_auto_20180824_1025'), + ("journal", "0012_auto_20180824_1025"), ] operations = [ migrations.AddField( - model_name='journal', - name='nav_collections', + model_name="journal", + name="nav_collections", field=models.BooleanField(default=False), ), ] diff --git a/src/journal/migrations/0014_journal_disable_metrics_display.py b/src/journal/migrations/0014_journal_disable_metrics_display.py index a1f4a34a2c..37cb494be5 100644 --- a/src/journal/migrations/0014_journal_disable_metrics_display.py +++ b/src/journal/migrations/0014_journal_disable_metrics_display.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0013_journal_nav_collections'), + ("journal", "0013_journal_nav_collections"), ] operations = [ migrations.AddField( - model_name='journal', - name='disable_metrics_display', + model_name="journal", + name="disable_metrics_display", field=models.BooleanField(default=False), ), ] diff --git a/src/journal/migrations/0015_auto_20181108_1220.py b/src/journal/migrations/0015_auto_20181108_1220.py index 4656f8b3b5..a04e9ba238 100644 --- a/src/journal/migrations/0015_auto_20181108_1220.py +++ b/src/journal/migrations/0015_auto_20181108_1220.py @@ -8,48 +8,89 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0014_journal_disable_metrics_display'), + ("journal", "0014_journal_disable_metrics_display"), ] operations = [ migrations.AddField( - model_name='journal', - name='disable_article_images', + model_name="journal", + name="disable_article_images", field=models.BooleanField( default=False, - help_text='This field has been deprecated in v1.4.3', + help_text="This field has been deprecated in v1.4.3", ), ), migrations.AlterField( - model_name='issue', - name='cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="issue", + name="cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='issue', - name='large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.issue_large_image_path), + model_name="issue", + name="large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.issue_large_image_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_cover_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_cover_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_large_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_large_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='header_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/vol/janeway/src/media'), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="header_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0016_journal_full_width_navbar.py b/src/journal/migrations/0016_journal_full_width_navbar.py index a7f46890ce..2a84fb82d5 100644 --- a/src/journal/migrations/0016_journal_full_width_navbar.py +++ b/src/journal/migrations/0016_journal_full_width_navbar.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0015_auto_20181108_1220'), + ("journal", "0015_auto_20181108_1220"), ] operations = [ migrations.AddField( - model_name='journal', - name='full_width_navbar', + model_name="journal", + name="full_width_navbar", field=models.BooleanField(default=False), ), ] diff --git a/src/journal/migrations/0017_file_fields_for_the_last_time.py b/src/journal/migrations/0017_file_fields_for_the_last_time.py index 98faf3906f..20c9477ebc 100644 --- a/src/journal/migrations/0017_file_fields_for_the_last_time.py +++ b/src/journal/migrations/0017_file_fields_for_the_last_time.py @@ -8,40 +8,75 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0016_journal_full_width_navbar'), + ("journal", "0016_journal_full_width_navbar"), ] operations = [ migrations.AlterField( - model_name='issue', - name='cover_image', - field=core.model_utils.SVGImageField(blank=True, help_text='Image representing the the cover of a printed issue or volume', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="issue", + name="cover_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="Image representing the the cover of a printed issue or volume", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='issue', - name='large_image', - field=core.model_utils.SVGImageField(blank=True, help_text='landscape hero image used in the carousel and issue page', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.issue_large_image_path), + model_name="issue", + name="large_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="landscape hero image used in the carousel and issue page", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.issue_large_image_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_cover_image', - field=core.model_utils.SVGImageField(blank=True, help_text="The default cover image for journal issues and for the journal's listing on the press-level website.", null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_cover_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="The default cover image for journal issues and for the journal's listing on the press-level website.", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='default_large_image', - field=core.model_utils.SVGImageField(blank=True, help_text='The default background image for article openers and carousel items.', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_large_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="The default background image for article openers and carousel items.", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='favicon', - field=models.ImageField(blank=True, help_text='The tiny round or square image appearing in browser tabs before the webpage title', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="favicon", + field=models.ImageField( + blank=True, + help_text="The tiny round or square image appearing in browser tabs before the webpage title", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='journal', - name='header_image', - field=core.model_utils.SVGImageField(blank=True, help_text='The logo-sized image at the top of all pages, typically used for journal logos.', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="header_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="The logo-sized image at the top of all pages, typically used for journal logos.", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0018_journal_disable_html_downloads.py b/src/journal/migrations/0018_journal_disable_html_downloads.py index 434307927e..e811a7596f 100644 --- a/src/journal/migrations/0018_journal_disable_html_downloads.py +++ b/src/journal/migrations/0018_journal_disable_html_downloads.py @@ -6,18 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0017_file_fields_for_the_last_time'), + ("journal", "0017_file_fields_for_the_last_time"), ] operations = [ migrations.AddField( - model_name='journal', - name='disable_html_downloads', + model_name="journal", + name="disable_html_downloads", field=models.BooleanField( default=False, - help_text='Used to disable download links for HTML files on the Article page.', + help_text="Used to disable download links for HTML files on the Article page.", ), ), ] diff --git a/src/journal/migrations/0019_journal_view_pdf_button.py b/src/journal/migrations/0019_journal_view_pdf_button.py index f2439148c1..b98f739acc 100644 --- a/src/journal/migrations/0019_journal_view_pdf_button.py +++ b/src/journal/migrations/0019_journal_view_pdf_button.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0018_journal_disable_html_downloads'), + ("journal", "0018_journal_disable_html_downloads"), ] operations = [ migrations.AddField( - model_name='journal', - name='view_pdf_button', - field=models.BooleanField(default=False, help_text='Enables a "View PDF" link on article pages.'), + model_name="journal", + name="view_pdf_button", + field=models.BooleanField( + default=False, help_text='Enables a "View PDF" link on article pages.' + ), ), ] diff --git a/src/journal/migrations/0020_journal_is_secure.py b/src/journal/migrations/0020_journal_is_secure.py index 122a49b13c..a43ceaf1ec 100644 --- a/src/journal/migrations/0020_journal_is_secure.py +++ b/src/journal/migrations/0020_journal_is_secure.py @@ -12,13 +12,9 @@ def populate_journal_is_secure(apps, schema_editor): journals = Journal.objects.all() for journal in journals: - value = None - setting = Setting.objects.get( - group__name='general', - name='is_secure' - ) + setting = Setting.objects.get(group__name="general", name="is_secure") try: value = SettingValue.objects.get( @@ -35,7 +31,7 @@ def populate_journal_is_secure(apps, schema_editor): cursor.execute(SQL) value = cursor.fetchall()[0][1] - if value == 'on': + if value == "on": journal.is_secure = True else: journal.is_secure = False @@ -46,16 +42,20 @@ def populate_journal_is_secure(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0019_journal_view_pdf_button'), + ("journal", "0019_journal_view_pdf_button"), ] operations = [ migrations.AddField( - model_name='journal', - name='is_secure', - field=models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.'), + model_name="journal", + name="is_secure", + field=models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), + ), + migrations.RunPython( + populate_journal_is_secure, reverse_code=migrations.RunPython.noop ), - migrations.RunPython(populate_journal_is_secure, reverse_code=migrations.RunPython.noop) ] diff --git a/src/journal/migrations/0021_journal_enable_correspondence_authors.py b/src/journal/migrations/0021_journal_enable_correspondence_authors.py index 9aa9651d76..afd81fe188 100644 --- a/src/journal/migrations/0021_journal_enable_correspondence_authors.py +++ b/src/journal/migrations/0021_journal_enable_correspondence_authors.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0020_journal_is_secure'), + ("journal", "0020_journal_is_secure"), ] operations = [ migrations.AddField( - model_name='journal', - name='enable_correspondence_authors', + model_name="journal", + name="enable_correspondence_authors", field=models.BooleanField(default=True), ), ] diff --git a/src/journal/migrations/0022_journal_is_conference.py b/src/journal/migrations/0022_journal_is_conference.py index dccae93ec2..2a95f1ce85 100644 --- a/src/journal/migrations/0022_journal_is_conference.py +++ b/src/journal/migrations/0022_journal_is_conference.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0021_journal_enable_correspondence_authors'), + ("journal", "0021_journal_enable_correspondence_authors"), ] operations = [ migrations.AddField( - model_name='journal', - name='is_conference', + model_name="journal", + name="is_conference", field=models.BooleanField(default=False), ), ] diff --git a/src/journal/migrations/0023_issuegalley.py b/src/journal/migrations/0023_issuegalley.py index e4aaec39de..e690d23cf9 100644 --- a/src/journal/migrations/0023_issuegalley.py +++ b/src/journal/migrations/0023_issuegalley.py @@ -7,19 +7,36 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0025_merge_20190201_1533'), - ('journal', '0022_journal_is_conference'), + ("core", "0025_merge_20190201_1533"), + ("journal", "0022_journal_is_conference"), ] operations = [ migrations.CreateModel( - name='IssueGalley', + name="IssueGalley", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.File')), - ('issue', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='journal.Issue')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.File" + ), + ), + ( + "issue", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="journal.Issue" + ), + ), ], ), ] diff --git a/src/journal/migrations/0024_auto_20190304_0916.py b/src/journal/migrations/0024_auto_20190304_0916.py index 4d2a204fe5..f644d12160 100644 --- a/src/journal/migrations/0024_auto_20190304_0916.py +++ b/src/journal/migrations/0024_auto_20190304_0916.py @@ -7,15 +7,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0023_issuegalley'), + ("journal", "0023_issuegalley"), ] operations = [ migrations.AlterField( - model_name='issuegalley', - name='issue', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='galley', to='journal.Issue'), + model_name="issuegalley", + name="issue", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="galley", + to="journal.Issue", + ), ), ] diff --git a/src/journal/migrations/0025_auto_20190326_1252.py b/src/journal/migrations/0025_auto_20190326_1252.py index ff2575cae8..e10478dede 100644 --- a/src/journal/migrations/0025_auto_20190326_1252.py +++ b/src/journal/migrations/0025_auto_20190326_1252.py @@ -8,13 +8,13 @@ def copy_guest_editors(apps, schema_editor): - Account = apps.get_model('core', 'Account') - Issue = apps.get_model('journal', 'Issue') - IssueEditor = apps.get_model('journal', 'IssueEditor') + Account = apps.get_model("core", "Account") + Issue = apps.get_model("journal", "Issue") + IssueEditor = apps.get_model("journal", "IssueEditor") cursor = connection.cursor() - cursor.execute('''SELECT * FROM journal_issue_guest_editors;''') + cursor.execute("""SELECT * FROM journal_issue_guest_editors;""") for row in cursor.fetchall(): issue_pk = row[1] account_pk = row[2] @@ -25,44 +25,62 @@ def copy_guest_editors(apps, schema_editor): IssueEditor.objects.create( account=account_obj, issue=issue_obj, - role='Guest Editor', + role="Guest Editor", ) class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('journal', '0024_auto_20190304_0916'), + ("journal", "0024_auto_20190304_0916"), ] operations = [ migrations.CreateModel( - name='IssueEditor', + name="IssueEditor", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, - serialize=False, verbose_name='ID')), - ('role', - models.CharField(default='Guest Editor', max_length=255)), - ('account', - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("role", models.CharField(default="Guest Editor", max_length=255)), + ( + "account", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='issueeditor', - name='issue', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Issue'), + model_name="issueeditor", + name="issue", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Issue" + ), + ), + migrations.RunPython( + copy_guest_editors, reverse_code=migrations.RunPython.noop ), - migrations.RunPython(copy_guest_editors, - reverse_code=migrations.RunPython.noop), migrations.RemoveField( - model_name='issue', - name='guest_editors', + model_name="issue", + name="guest_editors", ), migrations.AddField( - model_name='issue', - name='editors', - field=models.ManyToManyField(blank=True, null=True, related_name='guest_editors', through='journal.IssueEditor', to=settings.AUTH_USER_MODEL), + model_name="issue", + name="editors", + field=models.ManyToManyField( + blank=True, + null=True, + related_name="guest_editors", + through="journal.IssueEditor", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/journal/migrations/0026_journal_keywords.py b/src/journal/migrations/0026_journal_keywords.py index 8e929a91ed..aac2240634 100644 --- a/src/journal/migrations/0026_journal_keywords.py +++ b/src/journal/migrations/0026_journal_keywords.py @@ -6,16 +6,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0032_auto_20190304_0916'), - ('journal', '0025_auto_20190326_1252'), + ("submission", "0032_auto_20190304_0916"), + ("journal", "0025_auto_20190326_1252"), ] operations = [ migrations.AddField( - model_name='journal', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, to='submission.Keyword', verbose_name='Discipline'), + model_name="journal", + name="keywords", + field=models.ManyToManyField( + blank=True, + null=True, + to="submission.Keyword", + verbose_name="Discipline", + ), ), ] diff --git a/src/journal/migrations/0027_auto_20190404_1358.py b/src/journal/migrations/0027_auto_20190404_1358.py index 3fd2f9fa64..77a2ec8554 100644 --- a/src/journal/migrations/0027_auto_20190404_1358.py +++ b/src/journal/migrations/0027_auto_20190404_1358.py @@ -6,30 +6,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0026_journal_keywords'), + ("journal", "0026_journal_keywords"), ] operations = [ migrations.AddField( - model_name='journal', - name='display_issue_number', + model_name="journal", + name="display_issue_number", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='journal', - name='display_issue_title', + model_name="journal", + name="display_issue_title", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='journal', - name='display_issue_volume', + model_name="journal", + name="display_issue_volume", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='journal', - name='display_issue_year', + model_name="journal", + name="display_issue_year", field=models.BooleanField(default=True), ), ] diff --git a/src/journal/migrations/0028_auto_20190405_1547.py b/src/journal/migrations/0028_auto_20190405_1547.py index 2d233d9bec..cac28fd829 100644 --- a/src/journal/migrations/0028_auto_20190405_1547.py +++ b/src/journal/migrations/0028_auto_20190405_1547.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0027_auto_20190404_1358'), + ("journal", "0027_auto_20190404_1358"), ] operations = [ migrations.AlterField( - model_name='issue', - name='order', + model_name="issue", + name="order", field=models.IntegerField(default=0), ), ] diff --git a/src/journal/migrations/0029_journal_code_unique.py b/src/journal/migrations/0029_journal_code_unique.py index e0ef200f7f..b156d7b966 100644 --- a/src/journal/migrations/0029_journal_code_unique.py +++ b/src/journal/migrations/0029_journal_code_unique.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0028_auto_20190405_1547'), + ("journal", "0028_auto_20190405_1547"), ] operations = [ migrations.AlterField( - model_name='journal', - name='code', + model_name="journal", + name="code", field=models.CharField(max_length=10, unique=True), ), ] diff --git a/src/journal/migrations/0030_increase_journal_code_length.py b/src/journal/migrations/0030_increase_journal_code_length.py index 0297ebda29..967fc1117b 100644 --- a/src/journal/migrations/0030_increase_journal_code_length.py +++ b/src/journal/migrations/0030_increase_journal_code_length.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0029_journal_code_unique'), + ("journal", "0029_journal_code_unique"), ] operations = [ migrations.AlterField( - model_name='journal', - name='code', + model_name="journal", + name="code", field=models.CharField(max_length=15, unique=True), ), ] diff --git a/src/journal/migrations/0031_issue_old_issue_type.py b/src/journal/migrations/0031_issue_old_issue_type.py index 4872728aa6..cce31f22cc 100644 --- a/src/journal/migrations/0031_issue_old_issue_type.py +++ b/src/journal/migrations/0031_issue_old_issue_type.py @@ -6,7 +6,7 @@ def preserve_issue_types(apps, schema_editor): - Issue = apps.get_model('journal', 'Issue') + Issue = apps.get_model("journal", "Issue") for issue in Issue.objects.all(): issue.old_issue_type, issue.issue_type = issue.issue_type, None @@ -14,7 +14,7 @@ def preserve_issue_types(apps, schema_editor): def restore_issue_types(apps, schema_editor): - Issue = apps.get_model('journal', 'Issue') + Issue = apps.get_model("journal", "Issue") for issue in Issue.objects.all(): issue.issue_type, issue.old_issue_type = issue.old_issue_type, None @@ -23,24 +23,30 @@ def restore_issue_types(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('journal', '0030_increase_journal_code_length'), - ('utils', '0011_upgrade_1_3_5'), + ("journal", "0030_increase_journal_code_length"), + ("utils", "0011_upgrade_1_3_5"), ] operations = [ migrations.AddField( - model_name='issue', - name='old_issue_type', + model_name="issue", + name="old_issue_type", field=models.CharField( - choices=[('Issue', 'Issue'), ('Collection', 'Collection')], - default='Issue', max_length=200, + choices=[("Issue", "Issue"), ("Collection", "Collection")], + default="Issue", + max_length=200, null=True, ), ), migrations.AlterField( - model_name='issue', - name='issue_type', - field=models.CharField(choices=[('Issue', 'Issue'), ('Collection', 'Collection')], default='Issue', max_length=200, null=True), + model_name="issue", + name="issue_type", + field=models.CharField( + choices=[("Issue", "Issue"), ("Collection", "Collection")], + default="Issue", + max_length=200, + null=True, + ), ), migrations.RunPython( preserve_issue_types, diff --git a/src/journal/migrations/0032_add_issue_type.py b/src/journal/migrations/0032_add_issue_type.py index 4422bdd4a6..5a354d3028 100644 --- a/src/journal/migrations/0032_add_issue_type.py +++ b/src/journal/migrations/0032_add_issue_type.py @@ -7,34 +7,60 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0031_issue_old_issue_type'), + ("journal", "0031_issue_old_issue_type"), ] operations = [ migrations.CreateModel( - name='IssueType', + name="IssueType", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('code', models.CharField(max_length=255)), - ('pretty_name', models.CharField(max_length=255)), - ('custom_plural', models.CharField(blank=True, max_length=255, null=True)), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("code", models.CharField(max_length=255)), + ("pretty_name", models.CharField(max_length=255)), + ( + "custom_plural", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), migrations.AlterField( - model_name='issue', - name='issue_type', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='journal.IssueType'), + model_name="issue", + name="issue_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="journal.IssueType", + ), ), migrations.AlterField( - model_name='issue', - name='old_issue_type', - field=models.CharField(blank=True, choices=[('Issue', 'Issue'), ('Collection', 'Collection')], default='Issue', max_length=200, null=True), + model_name="issue", + name="old_issue_type", + field=models.CharField( + blank=True, + choices=[("Issue", "Issue"), ("Collection", "Collection")], + default="Issue", + max_length=200, + null=True, + ), ), migrations.AlterUniqueTogether( - name='issuetype', - unique_together=set([('journal', 'code')]), + name="issuetype", + unique_together=set([("journal", "code")]), ), ] diff --git a/src/journal/migrations/0033_migrate_issue_types.py b/src/journal/migrations/0033_migrate_issue_types.py index 0debddb250..085a88efc3 100644 --- a/src/journal/migrations/0033_migrate_issue_types.py +++ b/src/journal/migrations/0033_migrate_issue_types.py @@ -14,31 +14,29 @@ def load_default_issue_types(apps, schema_editor): Journal = apps.get_model("journal", "Journal") journals = Journal.objects.all() with codecs.open( - os.path.join(settings.BASE_DIR, 'utils/install/issue_type.json'), - encoding='utf-8' + os.path.join(settings.BASE_DIR, "utils/install/issue_type.json"), + encoding="utf-8", ) as json_data: default_data = json.load(json_data) for journal in journals: for item in default_data: default_dict = { - 'pretty_name': item['fields'].get('pretty_name'), - 'custom_plural': item['fields'].get('custom_plural'), + "pretty_name": item["fields"].get("pretty_name"), + "custom_plural": item["fields"].get("custom_plural"), } - issue_type, created = IssueType.objects\ - .get_or_create( - journal=journal, - code=item['fields']["code"], - defaults=default_dict + issue_type, created = IssueType.objects.get_or_create( + journal=journal, code=item["fields"]["code"], defaults=default_dict ) class Migration(migrations.Migration): - dependencies = [ - ('journal', '0032_add_issue_type'), + ("journal", "0032_add_issue_type"), ] operations = [ - migrations.RunPython(load_default_issue_types, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + load_default_issue_types, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/journal/migrations/0034_migrate_issue_types.py b/src/journal/migrations/0034_migrate_issue_types.py index df588c83ed..774350087a 100644 --- a/src/journal/migrations/0034_migrate_issue_types.py +++ b/src/journal/migrations/0034_migrate_issue_types.py @@ -24,11 +24,12 @@ def migrate_current_issue_types(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0033_migrate_issue_types'), + ("journal", "0033_migrate_issue_types"), ] operations = [ - migrations.RunPython(migrate_current_issue_types, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + migrate_current_issue_types, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/journal/migrations/0035_journal_xsl.py b/src/journal/migrations/0035_journal_xsl.py index dd19f40bfe..beb084d476 100644 --- a/src/journal/migrations/0035_journal_xsl.py +++ b/src/journal/migrations/0035_journal_xsl.py @@ -8,19 +8,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0034_migrate_issue_types'), - ('core', '0037_journal_xsl_files'), - - + ("journal", "0034_migrate_issue_types"), + ("core", "0037_journal_xsl_files"), ] operations = [ migrations.AddField( - model_name='journal', - name='xsl', - field=models.ForeignKey(default=journal.models.default_xsl, on_delete=django.db.models.deletion.SET_DEFAULT, to='core.XSLFile'), + model_name="journal", + name="xsl", + field=models.ForeignKey( + default=journal.models.default_xsl, + on_delete=django.db.models.deletion.SET_DEFAULT, + to="core.XSLFile", + ), preserve_default=False, ), ] diff --git a/src/journal/migrations/0036_set_default_xslt.py b/src/journal/migrations/0036_set_default_xslt.py index 48987a4296..93ba8c5e07 100644 --- a/src/journal/migrations/0036_set_default_xslt.py +++ b/src/journal/migrations/0036_set_default_xslt.py @@ -14,9 +14,8 @@ def set_default_xsl(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0035_journal_xsl'), + ("journal", "0035_journal_xsl"), ] operations = [ diff --git a/src/journal/migrations/0037_auto_20200116_1201.py b/src/journal/migrations/0037_auto_20200116_1201.py index 25d852671b..762e9ce2e6 100644 --- a/src/journal/migrations/0037_auto_20200116_1201.py +++ b/src/journal/migrations/0037_auto_20200116_1201.py @@ -8,16 +8,16 @@ class Migration(migrations.Migration): - - dependencies = [ - ('journal', '0036_set_default_xslt'), - ('core', '0038_xslt_1-3-8') - ] + dependencies = [("journal", "0036_set_default_xslt"), ("core", "0038_xslt_1-3-8")] operations = [ migrations.AlterField( - model_name='journal', - name='xsl', - field=models.ForeignKey(default=journal.models.default_xsl, on_delete=django.db.models.deletion.SET_DEFAULT, to='core.XSLFile'), + model_name="journal", + name="xsl", + field=models.ForeignKey( + default=journal.models.default_xsl, + on_delete=django.db.models.deletion.SET_DEFAULT, + to="core.XSLFile", + ), ), ] diff --git a/src/journal/migrations/0038_auto_20200428_0803.py b/src/journal/migrations/0038_auto_20200428_0803.py index 96d4982ed5..71fbab9fe5 100644 --- a/src/journal/migrations/0038_auto_20200428_0803.py +++ b/src/journal/migrations/0038_auto_20200428_0803.py @@ -8,16 +8,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0037_journal_xsl_files'), - ('journal', '0037_auto_20200116_1201'), + ("core", "0037_journal_xsl_files"), + ("journal", "0037_auto_20200116_1201"), ] operations = [ migrations.AlterField( - model_name='journal', - name='xsl', - field=models.ForeignKey(default=journal.models.default_xsl, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='default_xsl', to='core.XSLFile'), + model_name="journal", + name="xsl", + field=models.ForeignKey( + default=journal.models.default_xsl, + on_delete=django.db.models.deletion.SET_DEFAULT, + related_name="default_xsl", + to="core.XSLFile", + ), ), ] diff --git a/src/journal/migrations/0039_journal_disable_front_end.py b/src/journal/migrations/0039_journal_disable_front_end.py index aab0ab512a..4057645f71 100644 --- a/src/journal/migrations/0039_journal_disable_front_end.py +++ b/src/journal/migrations/0039_journal_disable_front_end.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0038_auto_20200428_0803'), + ("journal", "0038_auto_20200428_0803"), ] operations = [ migrations.AddField( - model_name='journal', - name='disable_front_end', + model_name="journal", + name="disable_front_end", field=models.BooleanField(default=False), ), ] diff --git a/src/journal/migrations/0040_auto_20200805_1923.py b/src/journal/migrations/0040_auto_20200805_1923.py index f7dc774221..2e19d7a644 100644 --- a/src/journal/migrations/0040_auto_20200805_1923.py +++ b/src/journal/migrations/0040_auto_20200805_1923.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0039_journal_disable_front_end'), + ("journal", "0039_journal_disable_front_end"), ] operations = [ migrations.AlterField( - model_name='issue', - name='issue', - field=models.CharField(default='1', max_length=255), + model_name="issue", + name="issue", + field=models.CharField(default="1", max_length=255), ), ] diff --git a/src/journal/migrations/0041_issue_short_description.py b/src/journal/migrations/0041_issue_short_description.py index 789c476198..c40fa6ca25 100644 --- a/src/journal/migrations/0041_issue_short_description.py +++ b/src/journal/migrations/0041_issue_short_description.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0040_auto_20200805_1923'), + ("journal", "0040_auto_20200805_1923"), ] operations = [ migrations.AddField( - model_name='issue', - name='short_description', + model_name="issue", + name="short_description", field=models.CharField(blank=True, max_length=600, null=True), ), ] diff --git a/src/journal/migrations/0042_auto_20210310_1453.py b/src/journal/migrations/0042_auto_20210310_1453.py index 89a0ea8df9..26739e5167 100644 --- a/src/journal/migrations/0042_auto_20210310_1453.py +++ b/src/journal/migrations/0042_auto_20210310_1453.py @@ -6,30 +6,37 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0041_issue_short_description'), + ("journal", "0041_issue_short_description"), ] operations = [ migrations.AddField( - model_name='journal', - name='contact_info_cy', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_cy", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AddField( - model_name='journal', - name='contact_info_de', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_de", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AddField( - model_name='journal', - name='contact_info_en', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_en", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AddField( - model_name='journal', - name='contact_info_fr', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_fr", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), ] diff --git a/src/journal/migrations/0043_auto_20210518_1616.py b/src/journal/migrations/0043_auto_20210518_1616.py index c5ddcc220c..96ff566847 100644 --- a/src/journal/migrations/0043_auto_20210518_1616.py +++ b/src/journal/migrations/0043_auto_20210518_1616.py @@ -6,25 +6,35 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0042_auto_20210310_1453'), + ("journal", "0042_auto_20210310_1453"), ] operations = [ migrations.AlterField( - model_name='journal', - name='is_remote', - field=models.BooleanField(default=False, help_text='When enabled the journal is marked as not hosted in Janeway.'), + model_name="journal", + name="is_remote", + field=models.BooleanField( + default=False, + help_text="When enabled the journal is marked as not hosted in Janeway.", + ), ), migrations.AlterField( - model_name='journal', - name='remote_submit_url', - field=models.URLField(blank=True, help_text='If the journal is remote you can link to its submission page.', null=True), + model_name="journal", + name="remote_submit_url", + field=models.URLField( + blank=True, + help_text="If the journal is remote you can link to its submission page.", + null=True, + ), ), migrations.AlterField( - model_name='journal', - name='remote_view_url', - field=models.URLField(blank=True, help_text='If the journal is remote you can link to its home page.', null=True), + model_name="journal", + name="remote_view_url", + field=models.URLField( + blank=True, + help_text="If the journal is remote you can link to its home page.", + null=True, + ), ), ] diff --git a/src/journal/migrations/0044_journal_contact_info_nl.py b/src/journal/migrations/0044_journal_contact_info_nl.py index b58de59e90..3b7ad566df 100644 --- a/src/journal/migrations/0044_journal_contact_info_nl.py +++ b/src/journal/migrations/0044_journal_contact_info_nl.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0043_auto_20210518_1616'), + ("journal", "0043_auto_20210518_1616"), ] operations = [ migrations.AddField( - model_name='journal', - name='contact_info_nl', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_nl", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), ] diff --git a/src/journal/migrations/0045_auto_20210721_1212.py b/src/journal/migrations/0045_auto_20210721_1212.py index 6ca84a29fb..327528ce72 100644 --- a/src/journal/migrations/0045_auto_20210721_1212.py +++ b/src/journal/migrations/0045_auto_20210721_1212.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0044_journal_contact_info_nl'), + ("journal", "0044_journal_contact_info_nl"), ] operations = [ migrations.AlterField( - model_name='journal', - name='is_remote', - field=models.BooleanField(default=False, help_text='When enabled, the journal is marked as not hosted in Janeway.'), + model_name="journal", + name="is_remote", + field=models.BooleanField( + default=False, + help_text="When enabled, the journal is marked as not hosted in Janeway.", + ), ), ] diff --git a/src/journal/migrations/0046_auto_20210922_1436.py b/src/journal/migrations/0046_auto_20210922_1436.py index 242c049c26..64c7847aa2 100644 --- a/src/journal/migrations/0046_auto_20210922_1436.py +++ b/src/journal/migrations/0046_auto_20210922_1436.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0045_auto_20210721_1212'), + ("journal", "0045_auto_20210721_1212"), ] operations = [ migrations.AlterField( - model_name='journal', - name='code', - field=models.CharField(help_text='Short acronym for the journal. Used as part of the journal URLin path mode and to uniquely identify the journal', max_length=24, unique=True), + model_name="journal", + name="code", + field=models.CharField( + help_text="Short acronym for the journal. Used as part of the journal URLin path mode and to uniquely identify the journal", + max_length=24, + unique=True, + ), ), ] diff --git a/src/journal/migrations/0047_auto_20220107_1212.py b/src/journal/migrations/0047_auto_20220107_1212.py index 9314bf0d43..345bead4f5 100644 --- a/src/journal/migrations/0047_auto_20220107_1212.py +++ b/src/journal/migrations/0047_auto_20220107_1212.py @@ -5,23 +5,25 @@ from django.db import migrations, models from django.conf import settings + def remove_stale_domains(apps, schema_editor): - if hasattr(settings, 'URL_CONFIG') and settings.URL_CONFIG == 'path': - Journal = apps.get_model('journal', 'Journal') + if hasattr(settings, "URL_CONFIG") and settings.URL_CONFIG == "path": + Journal = apps.get_model("journal", "Journal") Journal.objects.all().update(domain=None) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('journal', '0046_auto_20210922_1436'), + ("journal", "0046_auto_20210922_1436"), ] operations = [ migrations.AlterField( - model_name='journal', - name='domain', + model_name="journal", + name="domain", field=models.CharField(blank=True, max_length=255, null=True, unique=True), ), migrations.RunPython( - remove_stale_domains, reverse_code=migrations.RunPython.noop), + remove_stale_domains, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/journal/migrations/0047_issue_code.py b/src/journal/migrations/0047_issue_code.py index 08189efdff..4553dfe925 100644 --- a/src/journal/migrations/0047_issue_code.py +++ b/src/journal/migrations/0047_issue_code.py @@ -6,19 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0046_auto_20210922_1436'), + ("journal", "0046_auto_20210922_1436"), ] operations = [ migrations.AddField( - model_name='issue', - name='code', - field=models.SlugField(blank=True, help_text="An optional alphanumeric code (Slug) used to generate a verbose url for this issue. e.g: 'winter-special-issue'.", max_length=700, null=True), + model_name="issue", + name="code", + field=models.SlugField( + blank=True, + help_text="An optional alphanumeric code (Slug) used to generate a verbose url for this issue. e.g: 'winter-special-issue'.", + max_length=700, + null=True, + ), ), migrations.AlterUniqueTogether( - name='issue', - unique_together=set([('journal', 'code')]), + name="issue", + unique_together=set([("journal", "code")]), ), ] diff --git a/src/journal/migrations/0048_merge_20220110_1933.py b/src/journal/migrations/0048_merge_20220110_1933.py index bafcb3b950..50ac708bde 100644 --- a/src/journal/migrations/0048_merge_20220110_1933.py +++ b/src/journal/migrations/0048_merge_20220110_1933.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0047_issue_code'), - ('journal', '0047_auto_20220107_1212'), + ("journal", "0047_issue_code"), + ("journal", "0047_auto_20220107_1212"), ] - operations = [ - ] + operations = [] diff --git a/src/journal/migrations/0049_auto_20220303_1636.py b/src/journal/migrations/0049_auto_20220303_1636.py index f87f6f76d5..f375c79808 100644 --- a/src/journal/migrations/0049_auto_20220303_1636.py +++ b/src/journal/migrations/0049_auto_20220303_1636.py @@ -6,20 +6,22 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0048_merge_20220110_1933'), + ("journal", "0048_merge_20220110_1933"), ] operations = [ migrations.AddField( - model_name='journal', - name='display_article_number', - field=models.BooleanField(default=False, help_text='Whether to display article numbers. Article numbers are distinct from article ID and can be set in Edit Metadata.'), + model_name="journal", + name="display_article_number", + field=models.BooleanField( + default=False, + help_text="Whether to display article numbers. Article numbers are distinct from article ID and can be set in Edit Metadata.", + ), ), migrations.AddField( - model_name='journal', - name='display_article_page_numbers', + model_name="journal", + name="display_article_page_numbers", field=models.BooleanField(default=True), ), ] diff --git a/src/journal/migrations/0050_issue_last_modified.py b/src/journal/migrations/0050_issue_last_modified.py index 7da326c7c0..ae0739518e 100644 --- a/src/journal/migrations/0050_issue_last_modified.py +++ b/src/journal/migrations/0050_issue_last_modified.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0049_auto_20220303_1636'), + ("journal", "0049_auto_20220303_1636"), ] operations = [ migrations.AddField( - model_name='issue', - name='last_modified', + model_name="issue", + name="last_modified", field=models.DateTimeField(auto_now=True), ), ] diff --git a/src/journal/migrations/0051_journal_is_archived.py b/src/journal/migrations/0051_journal_is_archived.py index 006189adb0..219d269899 100644 --- a/src/journal/migrations/0051_journal_is_archived.py +++ b/src/journal/migrations/0051_journal_is_archived.py @@ -6,20 +6,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0050_issue_last_modified'), + ("journal", "0050_issue_last_modified"), ] operations = [ migrations.AddField( - model_name='journal', - name='is_archived', - field=models.BooleanField(default=False, help_text='The journal is no longer publishing. This is only used as part of the journal metadata.'), + model_name="journal", + name="is_archived", + field=models.BooleanField( + default=False, + help_text="The journal is no longer publishing. This is only used as part of the journal metadata.", + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title', - field=models.CharField(null=True, blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300), + model_name="issue", + name="cached_display_title", + field=models.CharField( + null=True, + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + ), ), ] diff --git a/src/journal/migrations/0052_add_fields_to_fixedpubcheckitems_and_issue.py b/src/journal/migrations/0052_add_fields_to_fixedpubcheckitems_and_issue.py index b3837792c0..dfdd317af9 100644 --- a/src/journal/migrations/0052_add_fields_to_fixedpubcheckitems_and_issue.py +++ b/src/journal/migrations/0052_add_fields_to_fixedpubcheckitems_and_issue.py @@ -15,8 +15,9 @@ SQL = "ALTER TABLE journal_issue ADD COLUMN cached_display_title varchar(300) NULL" + def add_cached_display_title(apps, schema_editor): - """ Tries to add this column for installs affected by 22b1058 + """Tries to add this column for installs affected by 22b1058 https://github.com/birkbeckctp/janeway/commit/22b105843c9d030f43434813a6553f9d9361af6b This commit changed migration history, which has led installations to be in @@ -32,7 +33,7 @@ def add_cached_display_title(apps, schema_editor): for installatations upgrading from 1.4.2.1 onto 1.4.3 (they would have missed the change above). New installations or installations coming directly from =<1.4.2 this will fail, so we handle the exception. - """ + """ cursor = connection.cursor() try: cursor.execute(SQL) @@ -40,10 +41,10 @@ def add_cached_display_title(apps, schema_editor): # Column already exists pass -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('submission', '0050_support_ordered_keywords'), + ("submission", "0050_support_ordered_keywords"), ] @@ -51,19 +52,27 @@ class Migration(migrations.Migration): atomic = False dependencies = [ - ('journal', '0051_journal_is_archived'), + ("journal", "0051_journal_is_archived"), ] operations = [ - migrations.RunPython(add_cached_display_title, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + add_cached_display_title, reverse_code=migrations.RunPython.noop + ), migrations.AddField( - model_name='fixedpubcheckitems', - name='select_open_reviews', + model_name="fixedpubcheckitems", + name="select_open_reviews", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='issue', - name='doi', - field=models.CharField(blank=True, help_text='The DOI (not URL) to be registered for the issue when registering articles that are part of this issue. If you have enabled issue autoregistration in your settings, this field should not be entered manually.', max_length=255, null=True, verbose_name='DOI'), + model_name="issue", + name="doi", + field=models.CharField( + blank=True, + help_text="The DOI (not URL) to be registered for the issue when registering articles that are part of this issue. If you have enabled issue autoregistration in your settings, this field should not be entered manually.", + max_length=255, + null=True, + verbose_name="DOI", + ), ), ] diff --git a/src/journal/migrations/0053_display_article_images_settings.py b/src/journal/migrations/0053_display_article_images_settings.py index 218b976d3f..04da0dd624 100644 --- a/src/journal/migrations/0053_display_article_images_settings.py +++ b/src/journal/migrations/0053_display_article_images_settings.py @@ -10,9 +10,8 @@ def update_setting_values(apps, schema_editor): Setting = apps.get_model("core", "Setting") SettingGroup = apps.get_model("core", "SettingGroup") SettingValue = apps.get_model("core", "SettingValue") - setting_group, _ = SettingGroup.objects.get_or_create( - name="article") - call_command('load_default_settings') + setting_group, _ = SettingGroup.objects.get_or_create(name="article") + call_command("load_default_settings") thumb_setting, c = Setting.objects.get_or_create(name="disable_article_thumbnails") large_image_setting = Setting.objects.get(name="disable_article_large_image") @@ -31,11 +30,12 @@ def update_setting_values(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0052_add_fields_to_fixedpubcheckitems_and_issue'), + ("journal", "0052_add_fields_to_fixedpubcheckitems_and_issue"), ] operations = [ - migrations.RunPython(update_setting_values, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + update_setting_values, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/journal/migrations/0054a_journal_press_image_override_temp.py b/src/journal/migrations/0054a_journal_press_image_override_temp.py index 1f50a3adb4..884699e7f5 100644 --- a/src/journal/migrations/0054a_journal_press_image_override_temp.py +++ b/src/journal/migrations/0054a_journal_press_image_override_temp.py @@ -13,18 +13,18 @@ def copy_press_image_override_temp(apps, schema_editor): - Journal = apps.get_model('journal', 'Journal') + Journal = apps.get_model("journal", "Journal") for journal in Journal.objects.all(): if journal.press_image_override: file_path = os.path.join( settings.BASE_DIR, - 'files', - 'journals', + "files", + "journals", str(journal.pk), str(journal.press_image_override.uuid_filename), ) if os.path.isfile(file_path): - with open(file_path, 'rb') as file: + with open(file_path, "rb") as file: image_file = ContentFile(file.read()) image_file.name = journal.press_image_override.original_filename journal.press_image_override_temp.save( @@ -34,16 +34,23 @@ def copy_press_image_override_temp(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('journal', '0053_display_article_images_settings'), + ("journal", "0053_display_article_images_settings"), ] operations = [ migrations.AddField( - model_name='journal', - name='press_image_override_temp', - field=core.model_utils.SVGImageField(blank=True, help_text='Replaces the press logo in the footer.', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="press_image_override_temp", + field=core.model_utils.SVGImageField( + blank=True, + help_text="Replaces the press logo in the footer.", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), + ), + migrations.RunPython( + copy_press_image_override_temp, reverse_code=migrations.RunPython.noop ), - migrations.RunPython(copy_press_image_override_temp, reverse_code=migrations.RunPython.noop), ] diff --git a/src/journal/migrations/0054b_remove_journal_press_image_override.py b/src/journal/migrations/0054b_remove_journal_press_image_override.py index faf12faaf6..a9e4396159 100644 --- a/src/journal/migrations/0054b_remove_journal_press_image_override.py +++ b/src/journal/migrations/0054b_remove_journal_press_image_override.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0054a_journal_press_image_override_temp'), + ("journal", "0054a_journal_press_image_override_temp"), ] operations = [ migrations.RemoveField( - model_name='journal', - name='press_image_override', + model_name="journal", + name="press_image_override", ), ] diff --git a/src/journal/migrations/0054c_auto_20220811_0958.py b/src/journal/migrations/0054c_auto_20220811_0958.py index ea6353dee0..4335453a7d 100644 --- a/src/journal/migrations/0054c_auto_20220811_0958.py +++ b/src/journal/migrations/0054c_auto_20220811_0958.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0054b_remove_journal_press_image_override'), + ("journal", "0054b_remove_journal_press_image_override"), ] operations = [ migrations.RenameField( - model_name='journal', - old_name='press_image_override_temp', - new_name='press_image_override', + model_name="journal", + old_name="press_image_override_temp", + new_name="press_image_override", ), ] diff --git a/src/journal/migrations/0055_issue_isbn.py b/src/journal/migrations/0055_issue_isbn.py index a834eadb73..b590162836 100644 --- a/src/journal/migrations/0055_issue_isbn.py +++ b/src/journal/migrations/0055_issue_isbn.py @@ -6,15 +6,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0054c_auto_20220811_0958'), + ("journal", "0054c_auto_20220811_0958"), ] operations = [ migrations.AddField( - model_name='issue', - name='isbn', - field=models.CharField(blank=True, help_text='An ISBN is relevant for non-serial collections such as conference proceedings', max_length=28, null=True, verbose_name='ISBN'), + model_name="issue", + name="isbn", + field=models.CharField( + blank=True, + help_text="An ISBN is relevant for non-serial collections such as conference proceedings", + max_length=28, + null=True, + verbose_name="ISBN", + ), ), ] diff --git a/src/journal/migrations/0056_auto_20230126_1317.py b/src/journal/migrations/0056_auto_20230126_1317.py index b43051f428..7758d8c837 100644 --- a/src/journal/migrations/0056_auto_20230126_1317.py +++ b/src/journal/migrations/0056_auto_20230126_1317.py @@ -6,20 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('journal', '0055_issue_isbn'), + ("journal", "0055_issue_isbn"), ] operations = [ migrations.AlterModelOptions( - name='issueeditor', - options={'ordering': ('sequence', 'account')}, + name="issueeditor", + options={"ordering": ("sequence", "account")}, ), migrations.AddField( - model_name='issueeditor', - name='sequence', - field=models.PositiveIntegerField(default=1, help_text='Provides for ordering of the Issue Editors.'), + model_name="issueeditor", + name="sequence", + field=models.PositiveIntegerField( + default=1, help_text="Provides for ordering of the Issue Editors." + ), ), ] diff --git a/src/journal/migrations/0056_display_issue_doi.py b/src/journal/migrations/0056_display_issue_doi.py index f7b6107d06..cf63f0a8d1 100644 --- a/src/journal/migrations/0056_display_issue_doi.py +++ b/src/journal/migrations/0056_display_issue_doi.py @@ -9,15 +9,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0055_issue_isbn'), + ("journal", "0055_issue_isbn"), ] operations = [ migrations.AddField( - model_name='journal', - name='display_issue_doi', + model_name="journal", + name="display_issue_doi", field=models.BooleanField(default=True), ), ] diff --git a/src/journal/migrations/0057_auto_20230208_1233.py b/src/journal/migrations/0057_auto_20230208_1233.py index a6aa315faa..a80697eeed 100644 --- a/src/journal/migrations/0057_auto_20230208_1233.py +++ b/src/journal/migrations/0057_auto_20230208_1233.py @@ -9,20 +9,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0056_display_issue_doi'), + ("journal", "0056_display_issue_doi"), ] operations = [ migrations.AddField( - model_name='journal', - name='contact_info_en_us', - field=models.TextField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_en_us", + field=models.TextField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='issue', - name='cover_image', - field=core.model_utils.SVGImageField(blank=True, help_text='Image representing the cover of a printed issue or volume', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="issue", + name="cover_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="Image representing the cover of a printed issue or volume", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0057_verbose_names_20230301_1914.py b/src/journal/migrations/0057_verbose_names_20230301_1914.py index 865a63c0a7..b66d703591 100644 --- a/src/journal/migrations/0057_verbose_names_20230301_1914.py +++ b/src/journal/migrations/0057_verbose_names_20230301_1914.py @@ -6,18 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0056_auto_20230126_1317'), + ("journal", "0056_auto_20230126_1317"), ] operations = [ migrations.AlterModelOptions( - name='fixedpubcheckitems', - options={'verbose_name_plural': 'Fixed pub check items'}, + name="fixedpubcheckitems", + options={"verbose_name_plural": "Fixed pub check items"}, ), migrations.AlterModelOptions( - name='notifications', - options={'verbose_name_plural': 'notifications'}, + name="notifications", + options={"verbose_name_plural": "notifications"}, ), ] diff --git a/src/journal/migrations/0058_merge_20230317_1530.py b/src/journal/migrations/0058_merge_20230317_1530.py index c031355dac..2e3f51253b 100644 --- a/src/journal/migrations/0058_merge_20230317_1530.py +++ b/src/journal/migrations/0058_merge_20230317_1530.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0057_auto_20230208_1233'), - ('journal', '0057_verbose_names_20230301_1914'), + ("journal", "0057_auto_20230208_1233"), + ("journal", "0057_verbose_names_20230301_1914"), ] - operations = [ - ] + operations = [] diff --git a/src/journal/migrations/0059_alter_prepublicationchecklistitem_completed_by.py b/src/journal/migrations/0059_alter_prepublicationchecklistitem_completed_by.py index 163d4ff4b4..84b5f43abc 100644 --- a/src/journal/migrations/0059_alter_prepublicationchecklistitem_completed_by.py +++ b/src/journal/migrations/0059_alter_prepublicationchecklistitem_completed_by.py @@ -6,16 +6,20 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('journal', '0058_merge_20230317_1530'), + ("journal", "0058_merge_20230317_1530"), ] operations = [ migrations.AlterField( - model_name='prepublicationchecklistitem', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="prepublicationchecklistitem", + name="completed_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/journal/migrations/0060_alter_journal_code.py b/src/journal/migrations/0060_alter_journal_code.py index 54cdfef2a4..c673c55112 100644 --- a/src/journal/migrations/0060_alter_journal_code.py +++ b/src/journal/migrations/0060_alter_journal_code.py @@ -4,15 +4,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0059_alter_prepublicationchecklistitem_completed_by'), + ("journal", "0059_alter_prepublicationchecklistitem_completed_by"), ] operations = [ migrations.AlterField( - model_name='journal', - name='code', - field=models.CharField(help_text='Short acronym for the journal. Used as part of the journal URLin path mode and to uniquely identify the journal', max_length=40, unique=True), + model_name="journal", + name="code", + field=models.CharField( + help_text="Short acronym for the journal. Used as part of the journal URLin path mode and to uniquely identify the journal", + max_length=40, + unique=True, + ), ), ] diff --git a/src/journal/migrations/0061_auto_20230816_1122.py b/src/journal/migrations/0061_auto_20230816_1122.py index e76a120c5c..c4d8b26a16 100644 --- a/src/journal/migrations/0061_auto_20230816_1122.py +++ b/src/journal/migrations/0061_auto_20230816_1122.py @@ -4,40 +4,75 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0060_alter_journal_code'), + ("journal", "0060_alter_journal_code"), ] operations = [ migrations.AddField( - model_name='issue', - name='cached_display_title_cy', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_cy", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title_de', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_de", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title_en', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_en", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title_en_us', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_en_us", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title_fr', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_fr", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='issue', - name='cached_display_title_nl', - field=models.CharField(blank=True, editable=False, help_text='Autogenerated cache of the display format of an issue title', max_length=300, null=True), + model_name="issue", + name="cached_display_title_nl", + field=models.CharField( + blank=True, + editable=False, + help_text="Autogenerated cache of the display format of an issue title", + max_length=300, + null=True, + ), ), ] diff --git a/src/journal/migrations/0062_auto_20240312_0922.py b/src/journal/migrations/0062_auto_20240312_0922.py index c3f9dbb379..ccd29bf3b7 100644 --- a/src/journal/migrations/0062_auto_20240312_0922.py +++ b/src/journal/migrations/0062_auto_20240312_0922.py @@ -5,65 +5,80 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0061_auto_20230816_1122'), + ("journal", "0061_auto_20230816_1122"), ] operations = [ migrations.AlterField( - model_name='issue', - name='issue_description', + model_name="issue", + name="issue_description", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='journal', - name='contact_info', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_cy', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_cy", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_de', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_de", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_en', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_en", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_en_us', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_en_us", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_fr', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_fr", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='contact_info_nl', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Contact Information'), + model_name="journal", + name="contact_info_nl", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Contact Information" + ), ), migrations.AlterField( - model_name='journal', - name='description', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Journal Description'), + model_name="journal", + name="description", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Journal Description" + ), ), migrations.AlterField( - model_name='prepublicationchecklistitem', - name='text', + model_name="prepublicationchecklistitem", + name="text", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='presetpublicationcheckitem', - name='text', + model_name="presetpublicationcheckitem", + name="text", field=core.model_utils.JanewayBleachField(), ), ] diff --git a/src/journal/migrations/0062_journal_display_issues_grouped_by_decade.py b/src/journal/migrations/0062_journal_display_issues_grouped_by_decade.py index 86d781b978..6593fcf114 100644 --- a/src/journal/migrations/0062_journal_display_issues_grouped_by_decade.py +++ b/src/journal/migrations/0062_journal_display_issues_grouped_by_decade.py @@ -4,15 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0061_auto_20230816_1122'), + ("journal", "0061_auto_20230816_1122"), ] operations = [ migrations.AddField( - model_name='journal', - name='display_issues_grouped_by_decade', - field=models.BooleanField(default=False, help_text='When enabled the issue page will group and display issues by decade.'), + model_name="journal", + name="display_issues_grouped_by_decade", + field=models.BooleanField( + default=False, + help_text="When enabled the issue page will group and display issues by decade.", + ), ), ] diff --git a/src/journal/migrations/0063_merge_20240327_1910.py b/src/journal/migrations/0063_merge_20240327_1910.py index 9423834b3f..acebf7f452 100644 --- a/src/journal/migrations/0063_merge_20240327_1910.py +++ b/src/journal/migrations/0063_merge_20240327_1910.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0062_auto_20240312_0922'), - ('journal', '0062_journal_display_issues_grouped_by_decade'), + ("journal", "0062_auto_20240312_0922"), + ("journal", "0062_journal_display_issues_grouped_by_decade"), ] - operations = [ - ] + operations = [] diff --git a/src/journal/migrations/0064_journal_default_editorial_team_image.py b/src/journal/migrations/0064_journal_default_editorial_team_image.py index fd0b14f710..d942f13893 100644 --- a/src/journal/migrations/0064_journal_default_editorial_team_image.py +++ b/src/journal/migrations/0064_journal_default_editorial_team_image.py @@ -7,16 +7,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0063_merge_20240327_1910'), - ('core', '0089_auto_20240328_1213') + ("journal", "0063_merge_20240327_1910"), + ("core", "0089_auto_20240328_1213"), ] operations = [ migrations.AddField( - model_name='journal', - name='default_profile_image', - field=core.model_utils.SVGImageField(blank=True, help_text='A default image displayed on the profile and editorial team pages when the user has no set profile image.', null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=journal.models.cover_images_upload_path), + model_name="journal", + name="default_profile_image", + field=core.model_utils.SVGImageField( + blank=True, + help_text="A default image displayed on the profile and editorial team pages when the user has no set profile image.", + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=journal.models.cover_images_upload_path, + ), ), ] diff --git a/src/journal/migrations/0065_issue_type_null_to_string.py b/src/journal/migrations/0065_issue_type_null_to_string.py index 24db27532b..456e5b2479 100644 --- a/src/journal/migrations/0065_issue_type_null_to_string.py +++ b/src/journal/migrations/0065_issue_type_null_to_string.py @@ -6,22 +6,20 @@ def migrate_null_values(apps, schema_editor): - model = apps.get_model('journal', 'IssueType') + model = apps.get_model("journal", "IssueType") fields = [ - 'custom_plural', + "custom_plural", ] migration_utils.store_empty_strings(model, fields) class Migration(migrations.Migration): - dependencies = [ - ('journal', '0064_journal_default_editorial_team_image'), + ("journal", "0064_journal_default_editorial_team_image"), ] operations = [ migrations.RunPython( - migrate_null_values, - reverse_code=migrations.RunPython.noop + migrate_null_values, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/journal/migrations/0065_prepub_send_notifications.py b/src/journal/migrations/0065_prepub_send_notifications.py index 19a96df25a..97918dfd3e 100644 --- a/src/journal/migrations/0065_prepub_send_notifications.py +++ b/src/journal/migrations/0065_prepub_send_notifications.py @@ -6,31 +6,30 @@ def replace_peer_reviewer_pub_notification(apps, schema_editor): values_to_replace = [ - '

Dear {{reviewer.full_name}}

An article that you served as peer reviewer for, \"{{ article.safe_title }}\", has been set for publication.

The article will be published on {{ article.date_published }}.

Regards,', + '

Dear {{reviewer.full_name}}

An article that you served as peer reviewer for, "{{ article.safe_title }}", has been set for publication.

The article will be published on {{ article.date_published }}.

Regards,', ] - replacement_value = '

Dear reviewers,

An article that you served as peer reviewer for, \"{{ article.safe_title }}\", has been set for publication.

The article will be published on {{ article.date_published }}.

Regards,' + replacement_value = '

Dear reviewers,

An article that you served as peer reviewer for, "{{ article.safe_title }}", has been set for publication.

The article will be published on {{ article.date_published }}.

Regards,' migration_utils.update_default_setting_values( apps, - setting_name='peer_reviewer_pub_notification', - group_name='email', + setting_name="peer_reviewer_pub_notification", + group_name="email", values_to_replace=values_to_replace, replacement_value=replacement_value, ) class Migration(migrations.Migration): - dependencies = [ - ('journal', '0064_journal_default_editorial_team_image'), + ("journal", "0064_journal_default_editorial_team_image"), ] operations = [ migrations.RenameField( - model_name='fixedpubcheckitems', - old_name='notify_the_author', - new_name='send_notifications', + model_name="fixedpubcheckitems", + old_name="notify_the_author", + new_name="send_notifications", ), migrations.RunPython( replace_peer_reviewer_pub_notification, diff --git a/src/journal/migrations/0066_issue_type_bleach_20240507_1359.py b/src/journal/migrations/0066_issue_type_bleach_20240507_1359.py index b094b17360..dc31969927 100644 --- a/src/journal/migrations/0066_issue_type_bleach_20240507_1359.py +++ b/src/journal/migrations/0066_issue_type_bleach_20240507_1359.py @@ -5,21 +5,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0065_prepub_send_notifications'), - ('journal', '0065_issue_type_null_to_string'), + ("journal", "0065_prepub_send_notifications"), + ("journal", "0065_issue_type_null_to_string"), ] operations = [ migrations.AlterField( - model_name='issuetype', - name='custom_plural', + model_name="issuetype", + name="custom_plural", field=core.model_utils.JanewayBleachCharField(blank=True, max_length=255), ), migrations.AlterField( - model_name='issuetype', - name='pretty_name', + model_name="issuetype", + name="pretty_name", field=core.model_utils.JanewayBleachCharField(max_length=255), ), ] diff --git a/src/journal/models.py b/src/journal/models.py index 1e7c44e33e..75254b05e8 100644 --- a/src/journal/models.py +++ b/src/journal/models.py @@ -20,7 +20,7 @@ TextField, F, ExpressionWrapper, - DateTimeField + DateTimeField, ) from django.db.models.functions import Concat, Coalesce from django.db.models.signals import post_save, m2m_changed @@ -33,9 +33,9 @@ from django.utils.translation import gettext from core import ( - files, - models as core_models, - workflow, + files, + models as core_models, + workflow, ) from core.file_system import JanewayFileSystemStorage from core.model_utils import ( @@ -64,8 +64,8 @@ # Use "Issue" for regular issues (rolling or periodic) # Use "Collection" for special collections ISSUE_TYPES = [ - ('Issue', 'Issue'), - ('Collection', 'Collection'), + ("Issue", "Issue"), + ("Collection", "Collection"), ] fs = JanewayFileSystemStorage() @@ -73,7 +73,7 @@ def cover_images_upload_path(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -82,13 +82,12 @@ def cover_images_upload_path(instance, filename): def default_xsl(): - return core_models.XSLFile.objects.get( - label=settings.DEFAULT_XSL_FILE_LABEL).pk + return core_models.XSLFile.objects.get(label=settings.DEFAULT_XSL_FILE_LABEL).pk def issue_large_image_path(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -97,79 +96,103 @@ def issue_large_image_path(instance, filename): class Journal(AbstractSiteModel): - code = models.CharField(max_length=40, unique=True, help_text=gettext( - 'Short acronym for the journal. Used as part of the journal URL' - 'in path mode and to uniquely identify the journal' - )) - current_issue = models.ForeignKey('Issue', related_name='current_issue', null=True, blank=True, - on_delete=models.SET_NULL) + code = models.CharField( + max_length=40, + unique=True, + help_text=gettext( + "Short acronym for the journal. Used as part of the journal URL" + "in path mode and to uniquely identify the journal" + ), + ) + current_issue = models.ForeignKey( + "Issue", + related_name="current_issue", + null=True, + blank=True, + on_delete=models.SET_NULL, + ) carousel = models.OneToOneField( - 'carousel.Carousel', - related_name='journal', + "carousel.Carousel", + related_name="journal", null=True, blank=True, on_delete=models.SET_NULL, ) thumbnail_image = models.ForeignKey( - 'core.File', + "core.File", null=True, blank=True, - related_name='thumbnail_image', + related_name="thumbnail_image", on_delete=models.SET_NULL, - help_text=gettext('The default thumbnail for articles, not to be ' - 'confused with \'Default cover image\'.'), + help_text=gettext( + "The default thumbnail for articles, not to be " + "confused with 'Default cover image'." + ), ) press_image_override = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('Replaces the press logo in the footer.'), + help_text=gettext("Replaces the press logo in the footer."), ) default_cover_image = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('The default cover image for journal issues and for ' - 'the journal\'s listing on the press-level website.'), + help_text=gettext( + "The default cover image for journal issues and for " + "the journal's listing on the press-level website." + ), ) default_large_image = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('The default background image for article openers ' - 'and carousel items.'), + help_text=gettext( + "The default background image for article openers " "and carousel items." + ), ) header_image = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('The logo-sized image at the top of all pages, ' - 'typically used for journal logos.'), + help_text=gettext( + "The logo-sized image at the top of all pages, " + "typically used for journal logos." + ), ) favicon = models.ImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('The tiny round or square image appearing in browser ' - 'tabs before the webpage title'), + help_text=gettext( + "The tiny round or square image appearing in browser " + "tabs before the webpage title" + ), ) default_profile_image = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text=gettext('A default image displayed on the profile and ' - 'editorial team pages when the user has no set ' - 'profile image.'), + help_text=gettext( + "A default image displayed on the profile and " + "editorial team pages when the user has no set " + "profile image." + ), ) # DEPRECATED "description" in favour of "journal_description" setting - description = JanewayBleachField(null=True, blank=True, verbose_name="Journal Description") - contact_info = JanewayBleachField(null=True, blank=True, verbose_name="Contact Information") + description = JanewayBleachField( + null=True, blank=True, verbose_name="Journal Description" + ) + contact_info = JanewayBleachField( + null=True, blank=True, verbose_name="Contact Information" + ) keywords = models.ManyToManyField( "submission.Keyword", blank=True, @@ -180,33 +203,37 @@ class Journal(AbstractSiteModel): disable_metrics_display = models.BooleanField(default=False) disable_article_images = models.BooleanField( default=False, - help_text=gettext('This field has been deprecated in v1.4.3'), + help_text=gettext("This field has been deprecated in v1.4.3"), ) enable_correspondence_authors = models.BooleanField(default=True) disable_html_downloads = models.BooleanField( default=False, - help_text='Used to disable download links for HTML files on the Article page.', + help_text="Used to disable download links for HTML files on the Article page.", ) full_width_navbar = models.BooleanField(default=False) is_remote = models.BooleanField( default=False, - help_text=gettext('When enabled, the journal is marked as not hosted in Janeway.'), + help_text=gettext( + "When enabled, the journal is marked as not hosted in Janeway." + ), ) is_conference = models.BooleanField(default=False) is_archived = models.BooleanField( default=False, help_text="The journal is no longer publishing. This is only used as " - "part of the journal metadata.", + "part of the journal metadata.", ) remote_submit_url = models.URLField( blank=True, null=True, - help_text=gettext('If the journal is remote you can link to its submission page.'), + help_text=gettext( + "If the journal is remote you can link to its submission page." + ), ) remote_view_url = models.URLField( blank=True, null=True, - help_text=gettext('If the journal is remote you can link to its home page.'), + help_text=gettext("If the journal is remote you can link to its home page."), ) view_pdf_button = models.BooleanField( default=False, @@ -226,7 +253,8 @@ class Journal(AbstractSiteModel): # (DEPRECATED)Boolean to determine if this journal has an XSLT file has_xslt = models.BooleanField(default=False) - xsl = models.ForeignKey('core.XSLFile', + xsl = models.ForeignKey( + "core.XSLFile", default=default_xsl, on_delete=models.SET_DEFAULT, related_name="default_xsl", @@ -242,7 +270,7 @@ class Journal(AbstractSiteModel): try: press_name = press_models.Press.get_press(None).name except Exception: - press_name = '' + press_name = "" # Issue Display display_issue_volume = models.BooleanField(default=True) @@ -252,23 +280,23 @@ class Journal(AbstractSiteModel): display_article_number = models.BooleanField( default=False, help_text=gettext( - "Whether to display article numbers. Article numbers are distinct " \ + "Whether to display article numbers. Article numbers are distinct " "from article ID and can be set in Edit Metadata.", - ) + ), ) display_article_page_numbers = models.BooleanField(default=True) display_issue_doi = models.BooleanField(default=True) display_issues_grouped_by_decade = models.BooleanField( default=False, - help_text='When enabled the issue page will group and display issues ' - 'by decade.', + help_text="When enabled the issue page will group and display issues " + "by decade.", ) disable_front_end = models.BooleanField(default=False) def __str__(self): if self.domain: - return u'{0}: {1}'.format(self.code, self.domain) + return "{0}: {1}".format(self.code, self.domain) else: return self.code @@ -276,73 +304,103 @@ def __str__(self): def override_cover(request, absolute=True): if request.journal.press_image_override: if absolute: - return os.path.join(settings.BASE_DIR, 'files', 'journals', str(request.journal.pk), - str(request.journal.press_image_override.uuid_filename)) + return os.path.join( + settings.BASE_DIR, + "files", + "journals", + str(request.journal.pk), + str(request.journal.press_image_override.uuid_filename), + ) else: - return os.path.join('files', 'journals', str(request.journal.pk), - str(request.journal.press_image_override.uuid_filename)) + return os.path.join( + "files", + "journals", + str(request.journal.pk), + str(request.journal.press_image_override.uuid_filename), + ) else: return None def get_setting(self, group_name, setting_name): - return setting_handler.get_setting(group_name, setting_name, self, create=False).processed_value + return setting_handler.get_setting( + group_name, setting_name, self, create=False + ).processed_value @property @cache(15) def name(self): try: - return setting_handler.get_setting('general', 'journal_name', self, default=True).value + return setting_handler.get_setting( + "general", "journal_name", self, default=True + ).value except IndexError: - self.name = 'Janeway Journal' + self.name = "Janeway Journal" return self.name @name.setter def name(self, value): - setting_handler.save_setting('general', 'journal_name', self, value) + setting_handler.save_setting("general", "journal_name", self, value) @property def publisher(self): - return setting_handler.get_setting('general', 'publisher_name', self, default=True).value + return setting_handler.get_setting( + "general", "publisher_name", self, default=True + ).value @publisher.setter def publisher(self, value): - setting_handler.save_setting('general', 'publisher_name', self, value) + setting_handler.save_setting("general", "publisher_name", self, value) @property @cache(120) def issn(self): - return setting_handler.get_setting('general', 'journal_issn', self, default=True).value + return setting_handler.get_setting( + "general", "journal_issn", self, default=True + ).value @mutable_cached_property def doi(self): - return setting_handler.get_setting('Identifiers', 'title_doi', self, default=True).value or None + return ( + setting_handler.get_setting( + "Identifiers", "title_doi", self, default=True + ).value + or None + ) @doi.setter def doi_setter(self, value): - setting_handler.save_setting('Identifiers', 'title_doi', self, value) + setting_handler.save_setting("Identifiers", "title_doi", self, value) @property @cache(120) def print_issn(self): - return setting_handler.get_setting('general', 'print_issn', self, default=True).value + return setting_handler.get_setting( + "general", "print_issn", self, default=True + ).value @property @cache(120) def use_crossref(self): - return setting_handler.get_setting('Identifiers', 'use_crossref', self, default=True).processed_value + return setting_handler.get_setting( + "Identifiers", "use_crossref", self, default=True + ).processed_value @issn.setter def issn(self, value): - setting_handler.save_setting('general', 'journal_issn', self, value) + setting_handler.save_setting("general", "journal_issn", self, value) @print_issn.setter def print_issn(self, value): - setting_handler.save_setting('general', 'print_issn', self, value) + setting_handler.save_setting("general", "print_issn", self, value) @property def slack_logging_enabled(self): - slack_webhook = setting_handler.get_setting('general', 'slack_webhook', self).value - slack_logging = setting_handler.get_setting('general', 'slack_logging', self).processed_value + slack_webhook = setting_handler.get_setting( + "general", "slack_webhook", self + ).value + slack_logging = setting_handler.get_setting( + "general", "slack_logging", self + ).processed_value if slack_logging and slack_webhook: return True @@ -360,26 +418,25 @@ def get_by_request(cls, request): if not obj: # Lookup by code try: - code = request.path.split('/')[1] + code = request.path.split("/")[1] obj = cls.objects.get(code=code) path = code except (IndexError, cls.DoesNotExist): pass return obj, path - def site_url(self, path="", query=''): - if self.domain and not settings.URL_CONFIG == 'path': - + def site_url(self, path="", query=""): + if self.domain and not settings.URL_CONFIG == "path": # Handle domain journal being browsed in path mode - site_path = f'/{self.code}' + site_path = f"/{self.code}" if path and path.startswith(site_path): - path = path[len(site_path):] + path = path[len(site_path) :] return logic.build_url( - netloc=self.domain, - scheme=self._get_scheme(), - port=None, - path=path, - query=query, + netloc=self.domain, + scheme=self._get_scheme(), + port=None, + path=path, + query=query, ) else: return self.press.site_path_url(self, path, query=query) @@ -401,7 +458,7 @@ def issue_type_plural_name(self): return None def serial_issues(self): - return Issue.objects.filter(journal=self, issue_type__code='issue') + return Issue.objects.filter(journal=self, issue_type__code="issue") @property def published_issues(self): @@ -417,7 +474,7 @@ def issues_by_decade(self, issues_to_sort=None): issues_to_sort = Issue.objects.filter( journal=self, date__lte=timezone.now(), - issue_type__code='issue', + issue_type__code="issue", ) for issue in issues_to_sort: issue_year = issue.date_published.year @@ -438,20 +495,21 @@ def issues_by_decade(self, issues_to_sort=None): return issue_decade_dict def editors(self): - """ Returns all users enrolled as editors for the journal + """Returns all users enrolled as editors for the journal :return: A queryset of core.models.Account """ return core_models.Account.objects.filter( - accountrole__role__slug="editor", - accountrole__journal=self, + accountrole__role__slug="editor", + accountrole__journal=self, ) def users_with_role(self, role): pks = [ - role.user.pk for role in core_models.AccountRole.objects.filter( + role.user.pk + for role in core_models.AccountRole.objects.filter( role__slug=role, journal=self, - ).prefetch_related('user') + ).prefetch_related("user") ] return core_models.Account.objects.filter(pk__in=pks) @@ -462,14 +520,18 @@ def users_with_role_count(self, role): ).count() def editor_pks(self): - return [[str(role.user.pk), str(role.user.pk)] for role in - core_models.AccountRole.objects.filter(role__slug='editor', journal=self)] + return [ + [str(role.user.pk), str(role.user.pk)] + for role in core_models.AccountRole.objects.filter( + role__slug="editor", journal=self + ) + ] def journal_users(self, objects=True): account_roles = core_models.AccountRole.objects.filter( journal=self, user__is_active=True, - ).select_related('user') + ).select_related("user") if objects: users = {role.user for role in account_roles} @@ -484,15 +546,22 @@ def editorial_groups(self): @property def editor_emails(self): - editor_roles = core_models.AccountRole.objects.filter(role__slug='editor', journal=self) + editor_roles = core_models.AccountRole.objects.filter( + role__slug="editor", journal=self + ) return [role.user.email for role in editor_roles] def next_featured_article_order(self): - orderings = [featured_article.sequence for featured_article in self.featuredarticle_set.all()] + orderings = [ + featured_article.sequence + for featured_article in self.featuredarticle_set.all() + ] return max(orderings) + 1 if orderings else 0 def next_contact_order(self): - contacts = core_models.Contacts.objects.filter(content_type__model='journal', object_id=self.pk) + contacts = core_models.Contacts.objects.filter( + content_type__model="journal", object_id=self.pk + ) orderings = [contact.sequence for contact in contacts] return max(orderings) + 1 if orderings else 0 @@ -503,11 +572,12 @@ def next_group_order(self): @property def scss_files(self): import journal.logic as journal_logic + return journal_logic.list_scss(self) @property def active_carousel(self): - """ Renders a carousel for the journal homepage. + """Renders a carousel for the journal homepage. :return: a tuple containing the active carousel and list of associated articles """ if self.carousel is None or not self.carousel.enabled: @@ -527,7 +597,7 @@ def next_pa_seq(self): return 0 def setup_directory(self): - directory = os.path.join(settings.BASE_DIR, 'files', 'journals', str(self.pk)) + directory = os.path.join(settings.BASE_DIR, "files", "journals", str(self.pk)) if not os.path.exists(directory): os.makedirs(directory) @@ -539,7 +609,9 @@ def workflow(self): def element_in_workflow(self, element_name): try: - element = core_models.WorkflowElement.objects.get(element_name=element_name, journal=self) + element = core_models.WorkflowElement.objects.get( + element_name=element_name, journal=self + ) if element in self.workflow().elements.all(): return True else: @@ -556,9 +628,13 @@ def published_articles(self): ) def article_keywords(self): - return submission_models.Keyword.objects.filter( - article__in=self.published_articles - ).order_by('word').distinct() + return ( + submission_models.Keyword.objects.filter( + article__in=self.published_articles + ) + .order_by("word") + .distinct() + ) @property def workflow_plugin_elements(self): @@ -569,139 +645,153 @@ def workflow_plugin_elements(self): @property def description_for_press(self): press_description = self.get_setting( - group_name='general', - setting_name='press_journal_description' + group_name="general", setting_name="press_journal_description" ) if press_description: return press_description else: return self.get_setting( - group_name='general', - setting_name='journal_description', + group_name="general", + setting_name="journal_description", ) def setup_default_review_form(self): default_review_form, c = review_models.ReviewForm.objects.get_or_create( journal=self, - name='Default Form', + name="Default Form", defaults={ - 'intro': 'Please complete the form below.', - 'thanks': 'Thank you for completing the review.', - } + "intro": "Please complete the form below.", + "thanks": "Thank you for completing the review.", + }, ) if c: main_element = review_models.ReviewFormElement.objects.create( - name='Review', - kind='textarea', + name="Review", + kind="textarea", required=True, order=1, - width='large-12 columns', - help_text=gettext('Please add as much detail as you can.'), + width="large-12 columns", + help_text=gettext("Please add as much detail as you can."), ) default_review_form.elements.add(main_element) def archive_published_articles(self): # Subquery will attempt to grab a DOI for this article. - doi_subquery = identifier_models.Identifier.objects.filter( - article=OuterRef('pk'), - id_type="doi", - enabled=True - ).annotate( - identifier_with_type=Concat(Value('DOI: '), 'identifier', - output_field=TextField()) - ).values('identifier_with_type')[:1] + doi_subquery = ( + identifier_models.Identifier.objects.filter( + article=OuterRef("pk"), id_type="doi", enabled=True + ) + .annotate( + identifier_with_type=Concat( + Value("DOI: "), "identifier", output_field=TextField() + ) + ) + .values("identifier_with_type")[:1] + ) # Subquery will attempt to grab a Pub ID for this article. - pubid_subquery = identifier_models.Identifier.objects.filter( - article=OuterRef('pk'), - id_type="pubid", - enabled=True - ).annotate( - identifier_with_type=Concat(Value('Pub ID: '), 'identifier', - output_field=TextField()) - ).values('identifier_with_type')[:1] + pubid_subquery = ( + identifier_models.Identifier.objects.filter( + article=OuterRef("pk"), id_type="pubid", enabled=True + ) + .annotate( + identifier_with_type=Concat( + Value("Pub ID: "), "identifier", output_field=TextField() + ) + ) + .values("identifier_with_type")[:1] + ) # Fetch published articles with either DOI, Pub ID or a warning. # Additionally, a subquery is run to get frozen authors without # running additional queries. - articles_with_identifiers = submission_models.Article.objects.filter( - journal=self, - stage=submission_models.STAGE_PUBLISHED - ).annotate( - preferred_identifier=Coalesce( - Subquery(doi_subquery, output_field=TextField()), - Subquery(pubid_subquery, output_field=TextField()), - Value('No DOI or Pub ID Registered', output_field=TextField()) - ), - frozen_authors=ExpressionWrapper( - db_functions.GroupConcat( - Concat( - Coalesce(F('frozenauthor__first_name'), Value('')), - Value(' '), - Coalesce(F('frozenauthor__middle_name'), Value('')), - Value(' '), - Coalesce(F('frozenauthor__last_name'), Value('')) + articles_with_identifiers = ( + submission_models.Article.objects.filter( + journal=self, stage=submission_models.STAGE_PUBLISHED + ) + .annotate( + preferred_identifier=Coalesce( + Subquery(doi_subquery, output_field=TextField()), + Subquery(pubid_subquery, output_field=TextField()), + Value("No DOI or Pub ID Registered", output_field=TextField()), + ), + frozen_authors=ExpressionWrapper( + db_functions.GroupConcat( + Concat( + Coalesce(F("frozenauthor__first_name"), Value("")), + Value(" "), + Coalesce(F("frozenauthor__middle_name"), Value("")), + Value(" "), + Coalesce(F("frozenauthor__last_name"), Value("")), + ), ), + output_field=TextField(), ), - output_field=TextField() ) - ).order_by( - '-date_published' + .order_by("-date_published") ) return articles_with_identifiers def rejected_and_archived_articles(self): - date_archived_subquery = submission_models.ArticleStageLog.objects.filter( - article=OuterRef('pk'), - stage_to='Archived' - ).order_by('date_time').values('date_time')[:1] + date_archived_subquery = ( + submission_models.ArticleStageLog.objects.filter( + article=OuterRef("pk"), stage_to="Archived" + ) + .order_by("date_time") + .values("date_time")[:1] + ) - return submission_models.Article.objects.filter( - journal=self, - stage__in=[ - submission_models.STAGE_REJECTED, - submission_models.STAGE_ARCHIVED, - ], - ).annotate( - frozen_authors=ExpressionWrapper( - db_functions.GroupConcat( - Concat( - Coalesce(F('frozenauthor__first_name'), Value('')), - Value(' '), - Coalesce(F('frozenauthor__middle_name'), Value('')), - Value(' '), - Coalesce(F('frozenauthor__last_name'), Value('')) + return ( + submission_models.Article.objects.filter( + journal=self, + stage__in=[ + submission_models.STAGE_REJECTED, + submission_models.STAGE_ARCHIVED, + ], + ) + .annotate( + frozen_authors=ExpressionWrapper( + db_functions.GroupConcat( + Concat( + Coalesce(F("frozenauthor__first_name"), Value("")), + Value(" "), + Coalesce(F("frozenauthor__middle_name"), Value("")), + Value(" "), + Coalesce(F("frozenauthor__last_name"), Value("")), + ), ), + output_field=TextField(), + ), + date_archived=Subquery( + date_archived_subquery, + output_field=DateTimeField(), ), - output_field=TextField() - ), - date_archived=Subquery( - date_archived_subquery, - output_field=DateTimeField(), ) - ).order_by( - '-date_declined' + .order_by("-date_declined") ) + class PinnedArticle(models.Model): journal = models.ForeignKey( Journal, on_delete=models.CASCADE, ) article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) sequence = models.PositiveIntegerField(default=0) class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): - return '{0}, {1}: {2}'.format(self.sequence, self.journal.code, self.article.title) + return "{0}, {1}: {2}".format( + self.sequence, self.journal.code, self.article.title + ) ISSUE_CODE_RE = re.compile("^[a-zA-Z0-9-_]+$") @@ -718,7 +808,9 @@ class Issue(AbstractLastModifiedModel): issue = models.CharField(max_length=255, default="1") issue_title = models.CharField(blank=True, max_length=300) cached_display_title = models.CharField( - null=True, blank=True, max_length=300, + null=True, + blank=True, + max_length=300, editable=False, help_text=gettext( "Autogenerated cache of the display format of an issue title" @@ -727,39 +819,50 @@ class Issue(AbstractLastModifiedModel): date = models.DateTimeField(default=timezone.now) order = models.IntegerField(default=0) issue_type = models.ForeignKey( - "journal.IssueType", blank=False, null=True, on_delete=models.SET_NULL) + "journal.IssueType", blank=False, null=True, on_delete=models.SET_NULL + ) # To be deprecated in 1.3.7 - old_issue_type = models.CharField(max_length=200, default='Issue', choices=ISSUE_TYPES, null=True, blank=True) + old_issue_type = models.CharField( + max_length=200, default="Issue", choices=ISSUE_TYPES, null=True, blank=True + ) issue_description = JanewayBleachField(blank=True, null=True) short_description = models.CharField(max_length=600, blank=True, null=True) cover_image = SVGImageField( - upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, + upload_to=cover_images_upload_path, + null=True, + blank=True, + storage=fs, help_text=gettext( "Image representing the cover of a printed issue or volume", - ) + ), ) large_image = SVGImageField( - upload_to=issue_large_image_path, null=True, blank=True, storage=fs, - help_text=gettext( - "landscape hero image used in the carousel and issue page" - ) + upload_to=issue_large_image_path, + null=True, + blank=True, + storage=fs, + help_text=gettext("landscape hero image used in the carousel and issue page"), ) # issue articles - articles = models.ManyToManyField('submission.Article', blank=True, null=True, related_name='issues') + articles = models.ManyToManyField( + "submission.Article", blank=True, null=True, related_name="issues" + ) # guest editors editors = models.ManyToManyField( - 'core.Account', + "core.Account", blank=True, null=True, - related_name='guest_editors', - through='IssueEditor', + related_name="guest_editors", + through="IssueEditor", ) code = models.SlugField( - max_length=700, null=True, blank=True, + max_length=700, + null=True, + blank=True, help_text=gettext( "An optional alphanumeric code (Slug) used to generate a verbose " " url for this issue. e.g: 'winter-special-issue'." @@ -770,12 +873,12 @@ class Issue(AbstractLastModifiedModel): max_length=255, blank=True, null=True, - verbose_name='DOI', - help_text='The DOI (not URL) to be registered for the issue when registering ' - 'articles that are part of this issue. If you have enabled issue ' - 'autoregistration in your settings, this field should not be ' - 'entered manually.', - ) + verbose_name="DOI", + help_text="The DOI (not URL) to be registered for the issue when registering " + "articles that are part of this issue. If you have enabled issue " + "autoregistration in your settings, this field should not be " + "entered manually.", + ) isbn = models.CharField( max_length=28, @@ -792,7 +895,7 @@ class Issue(AbstractLastModifiedModel): def doi_url(self): if self.doi: return f"https://doi.org/{self.doi}" - return '' + return "" @property def hero_image_url(self): @@ -801,12 +904,14 @@ def hero_image_url(self): elif self.journal.default_large_image: return self.journal.default_large_image.url else: - return '' + return "" @property def date_published(self): return datetime.datetime( - self.date.year, self.date.month, self.date.day, + self.date.year, + self.date.month, + self.date.day, tzinfo=self.date.tzinfo, ) @@ -815,8 +920,7 @@ def url(self): if self.is_serial: path = reverse("journal_issue", kwargs={"issue_id": self.pk}) else: - path = reverse( - "journal_collection", kwargs={"collection_id": self.pk}) + path = reverse("journal_collection", kwargs={"collection_id": self.pk}) return self.journal.site_url(path=path) @property @@ -834,11 +938,11 @@ def carousel_title(self): @property def carousel_image_resolver(self): - return 'news_file_download' + return "news_file_download" @property def is_serial(self): - if self.issue_type.code == 'issue': + if self.issue_type.code == "issue": return True else: return False @@ -846,26 +950,25 @@ def is_serial(self): @cached_property def display_title(self): return mark_safe( - self.cached_display_title - or self.update_display_title(save=True) + self.cached_display_title or self.update_display_title(save=True) ) def update_display_title(self, save=False): title = None - if self.issue_type and self.issue_type.code == 'issue': + if self.issue_type and self.issue_type.code == "issue": if save: self.save() return self.cached_display_title title = self.cached_display_title = self.pretty_issue_identifier else: - self.cached_display_title = '' + self.cached_display_title = "" title = self.issue_title return title def issue_title_parts(self, article=None): journal = self.journal - volume = issue = year = issue_title = article_number = page_numbers = '' + volume = issue = year = issue_title = article_number = page_numbers = "" if journal.display_issue_volume and self.volume: volume = "{%% trans 'Volume' %%} %s" % self.volume @@ -882,9 +985,9 @@ def issue_title_parts(self, article=None): page_numbers = article.page_range elif article.total_pages: if article.total_pages != 1: - label = gettext('pages') + label = gettext("pages") else: - label = gettext('page') + label = gettext("page") num_pages = str(article.total_pages) page_numbers = f"{num_pages} {label}" @@ -899,9 +1002,9 @@ def pretty_issue_identifier(self): @property def non_pretty_issue_identifier(self): - return Template( - " ".join((filter(None, self.issue_title_parts()))) - ).render(Context()) + return Template(" ".join((filter(None, self.issue_title_parts())))).render( + Context() + ) @property def manage_issue_list(self): @@ -915,12 +1018,16 @@ def manage_issue_list(self): for ordered_section in ordered_sections: article_list = list() - ordered_articles = ArticleOrdering.objects.filter(issue=self, section=ordered_section.section) + ordered_articles = ArticleOrdering.objects.filter( + issue=self, section=ordered_section.section + ) for article in ordered_articles: article_list.append(article.article) - articles = self.articles.filter(section=ordered_section.section, pk__in=article_pks) + articles = self.articles.filter( + section=ordered_section.section, pk__in=article_pks + ) for article in articles: if not article in article_list: @@ -932,7 +1039,9 @@ def manage_issue_list(self): for article in self.articles.all(): if not section_article_dict.get(article.section): article_list = list() - articles = self.articles.filter(section=article.section, pk__in=article_pks).order_by('section') + articles = self.articles.filter( + section=article.section, pk__in=article_pks + ).order_by("section") for article in articles: article_list.append(article) @@ -943,8 +1052,10 @@ def manage_issue_list(self): @property def all_sections(self): - ordered_sections = [order.section for order in SectionOrdering.objects.filter(issue=self)] - articles = self.articles.all().order_by('section') + ordered_sections = [ + order.section for order in SectionOrdering.objects.filter(issue=self) + ] + articles = self.articles.all().order_by("section") for article in articles: if not article.section in ordered_sections: @@ -981,16 +1092,16 @@ def issue_articles(self): ordered_list = list() for article in articles: - ordered_list.append({'article': article, 'order': self.get_article_order(article)}) + ordered_list.append( + {"article": article, "order": self.get_article_order(article)} + ) - return sorted(ordered_list, key=itemgetter('order')) + return sorted(ordered_list, key=itemgetter("order")) def structure(self): # This method is very inefficient and is not used in core anymore # Kept for backwards compatibility with 3rd party themes - logger.warning( - "Using 'Issue.structure' will be deprecated as of Janeway 1.4" - ) + logger.warning("Using 'Issue.structure' will be deprecated as of Janeway 1.4") structure = collections.OrderedDict() sections = self.all_sections @@ -1019,7 +1130,7 @@ def structure(self): return structure def get_sorted_articles(self, published_only=True): - """ Returns issue articles sorted by section and article order + """Returns issue articles sorted by section and article order Many fields are prefetched and annotated to handle large issues more eficiently. In particular, it annotates relevant SectionOrder and @@ -1038,20 +1149,25 @@ def get_sorted_articles(self, published_only=True): issue=Value(self.pk), ).values_list("order") - issue_articles = self.articles.prefetch_related( - 'authors', - 'frozenauthor_set', - 'manuscript_files', - ).select_related( - 'section', - ).annotate( - section_order=Subquery(section_order_subquery), - article_order=Subquery(article_order_subquery), - ).order_by( - "section_order", - "section__sequence", - "section__pk", - "article_order", + issue_articles = ( + self.articles.prefetch_related( + "authors", + "frozenauthor_set", + "manuscript_files", + ) + .select_related( + "section", + ) + .annotate( + section_order=Subquery(section_order_subquery), + article_order=Subquery(article_order_subquery), + ) + .order_by( + "section_order", + "section__sequence", + "section__pk", + "article_order", + ) ) if published_only: @@ -1078,7 +1194,9 @@ def get_article_order(self, article): return 0 def next_order(self): - orderings = [ordering.order for ordering in ArticleOrdering.objects.filter(issue=self)] + orderings = [ + ordering.order for ordering in ArticleOrdering.objects.filter(issue=self) + ] return max(orderings) + 1 if orderings else 0 @staticmethod @@ -1094,8 +1212,8 @@ def auto_increment_volume_issue(journal): try: latest_issue = Issue.objects.filter( journal=journal, - issue_type__code='issue', - ).latest('date') + issue_type__code="issue", + ).latest("date") # if no issues in journal, start at 1:1 except Issue.DoesNotExist: @@ -1124,15 +1242,13 @@ def is_published(self): return False def order_articles_in_sections(self, sort_field, order): - order_by_string = '{}{}'.format( - '-' if order == 'dsc' else '', + order_by_string = "{}{}".format( + "-" if order == "dsc" else "", sort_field, ) for section in self.all_sections: - section_articles = self.articles.filter( - section=section - ).order_by( + section_articles = self.articles.filter(section=section).order_by( order_by_string, ) ids_in_order = [section_article.pk for section_article in section_articles] @@ -1148,8 +1264,8 @@ def order_articles_in_sections(self, sort_field, order): def save(self, *args, **kwargs): # get the currently enabled languages for this journal or the default journal_languages = self.journal.get_setting( - 'general', - 'journal_languages', + "general", + "journal_languages", ) or [settings.LANGUAGE_CODE] for lang in journal_languages: @@ -1160,9 +1276,9 @@ def save(self, *args, **kwargs): def __str__(self): return ( - '{self.issue_type.pretty_name}: ' - '{self.issue}({self.volume}) ' - '{self.issue_title} ({self.date.year})'.format(self=self) + "{self.issue_type.pretty_name}: " + "{self.issue}({self.volume}) " + "{self.issue_title} ({self.date.year})".format(self=self) ) class Meta: @@ -1181,32 +1297,27 @@ class IssueType(models.Model): custom_plural = JanewayBleachCharField(max_length=255, blank=True) def __str__(self): - return ( - self.custom_plural - or self.pretty_name - or "{self.code}".format(self=self) - ) - + return self.custom_plural or self.pretty_name or "{self.code}".format(self=self) @property def plural_name(self): return self.custom_plural or "{self.pretty_name}s".format(self=self) class Meta: - unique_together = ('journal', 'code') + unique_together = ("journal", "code") class IssueGalley(models.Model): - FILES_PATH = 'issues' + FILES_PATH = "issues" file = models.ForeignKey( - 'core.File', + "core.File", on_delete=models.CASCADE, ) # An Issue can only have one galley at this time (PDF) issue = models.OneToOneField( - 'journal.Issue', - related_name='galley', + "journal.Issue", + related_name="galley", on_delete=models.CASCADE, ) @@ -1233,7 +1344,7 @@ def path_parts(self): class IssueEditor(models.Model): account = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) issue = models.ForeignKey( @@ -1242,15 +1353,14 @@ class IssueEditor(models.Model): ) role = models.CharField( max_length=255, - default='Guest Editor', + default="Guest Editor", ) sequence = models.PositiveIntegerField( - default=1, - help_text='Provides for ordering of the Issue Editors.' + default=1, help_text="Provides for ordering of the Issue Editors." ) class Meta: - ordering = ('sequence', 'account') + ordering = ("sequence", "account") def __str__(self): return "{user} {role}".format( @@ -1261,7 +1371,7 @@ def __str__(self): class SectionOrdering(models.Model): section = models.ForeignKey( - 'submission.Section', + "submission.Section", on_delete=models.CASCADE, ) issue = models.ForeignKey( @@ -1271,15 +1381,21 @@ class SectionOrdering(models.Model): order = models.PositiveIntegerField(default=1) def __str__(self): - return "{0}: {1}, ({2} {3}) {4}".format(self.order, self.issue.issue_title, self.issue.volume, self.issue.issue, self.section) + return "{0}: {1}, ({2} {3}) {4}".format( + self.order, + self.issue.issue_title, + self.issue.volume, + self.issue.issue, + self.section, + ) class Meta: - ordering = ('order', 'section') + ordering = ("order", "section") class ArticleOrdering(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) issue = models.ForeignKey( @@ -1287,14 +1403,14 @@ class ArticleOrdering(models.Model): on_delete=models.CASCADE, ) section = models.ForeignKey( - 'submission.Section', + "submission.Section", on_delete=models.CASCADE, ) order = models.PositiveIntegerField(default=1) class Meta: - unique_together = ('article', 'issue', 'section') - ordering = ('order', 'section') + unique_together = ("article", "issue", "section") + ordering = ("order", "section") def __str__(self): return "{0}: {1}, {2}".format(self.order, self.section, self.article.title) @@ -1302,7 +1418,7 @@ def __str__(self): class FixedPubCheckItems(models.Model): article = models.OneToOneField( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) @@ -1316,7 +1432,7 @@ class FixedPubCheckItems(models.Model): select_open_reviews = models.BooleanField(default=False) class Meta: - verbose_name_plural = 'Fixed pub check items' + verbose_name_plural = "Fixed pub check items" class PresetPublicationCheckItem(models.Model): @@ -1332,13 +1448,13 @@ class PresetPublicationCheckItem(models.Model): class PrePublicationChecklistItem(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) completed = models.BooleanField(default=False) completed_by = models.ForeignKey( - 'core.Account', + "core.Account", blank=True, null=True, on_delete=models.SET_NULL, @@ -1362,8 +1478,8 @@ class Meta: def notification_type(): return ( - ('submission', 'Submission'), - ('acceptance', 'Acceptance'), + ("submission", "Submission"), + ("acceptance", "Acceptance"), ) @@ -1373,7 +1489,7 @@ class Notifications(models.Model): on_delete=models.CASCADE, ) user = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) domain = models.CharField(max_length=100) @@ -1381,14 +1497,15 @@ class Notifications(models.Model): active = models.BooleanField(default=False) def __str__(self): - return '{0}, {1}: {2}'.format(self.journal, self.user, self.domain) + return "{0}, {1}: {2}".format(self.journal, self.user, self.domain) class Meta: - verbose_name_plural = 'notifications' + verbose_name_plural = "notifications" # Signals + @receiver(post_save, sender=Journal) def setup_default_section(sender, instance, created, **kwargs): if created: @@ -1396,8 +1513,8 @@ def setup_default_section(sender, instance, created, **kwargs): submission_models.Section.objects.get_or_create( journal=instance, number_of_reviewers=2, - name='Article', - plural='Articles' + name="Article", + plural="Articles", ) @@ -1435,13 +1552,15 @@ def setup_submission_items(sender, instance, created, **kwargs): def setup_default_form(sender, instance, created, **kwargs): # if this is a new journal and there is not default review for already # create a new one with a default review element. - if created and not review_models.ReviewForm.objects.filter( + if ( + created + and not review_models.ReviewForm.objects.filter( journal=instance, - ).exists(): + ).exists() + ): instance.setup_default_review_form() - @receiver(post_save, sender=Journal) def update_issue_display_title(sender, instance, created, **kwargs): for issue in Issue.objects.filter(journal=instance): @@ -1459,15 +1578,14 @@ def issue_articles_change(sender, **kwargs): When an article is removed from an issue this signal will delete any orderings for that article in the issue. """ - supported_actions = ['post_remove', 'post_add'] - issue_or_article = kwargs.get('instance') - action = kwargs.get('action') - update_side = kwargs.get('reverse') + supported_actions = ["post_remove", "post_add"] + issue_or_article = kwargs.get("instance") + action = kwargs.get("action") + update_side = kwargs.get("reverse") if issue_or_article and action in supported_actions: - object_pks = kwargs.get('pk_set', []) + object_pks = kwargs.get("pk_set", []) for object_pk in object_pks: - if update_side: article = issue_or_article issue = Issue.objects.get(pk=object_pk) @@ -1475,7 +1593,7 @@ def issue_articles_change(sender, **kwargs): article = submission_models.Article.objects.get(pk=object_pk) issue = issue_or_article - if action == 'post_remove': + if action == "post_remove": try: ordering = ArticleOrdering.objects.get( issue=issue, @@ -1503,7 +1621,7 @@ def issue_articles_change(sender, **kwargs): article.primary_issue = None article.save() - elif action == 'post_add': + elif action == "post_add": ArticleOrdering.objects.get_or_create( issue=issue, article=article, diff --git a/src/journal/templatetags/journal.py b/src/journal/templatetags/journal.py index 0ed3037dd0..ba2fb3fc19 100644 --- a/src/journal/templatetags/journal.py +++ b/src/journal/templatetags/journal.py @@ -11,7 +11,7 @@ def current_journal(context, queryset): :param queryset: A queryset with a journal FK :return: a queryset """ - request = context.get('request') + request = context.get("request") if not request.journal: return queryset @@ -27,7 +27,7 @@ def current_journal_count(context, queryset): :param queryset: A queryset with a journal FK :return: an integer """ - request = context.get('request') + request = context.get("request") if not request.journal: return queryset diff --git a/src/journal/tests/test_journal_frontend.py b/src/journal/tests/test_journal_frontend.py index 3dc7d92cc3..f15ac8edb5 100644 --- a/src/journal/tests/test_journal_frontend.py +++ b/src/journal/tests/test_journal_frontend.py @@ -13,29 +13,25 @@ class TestJournalSite(TestCase): - def setUp(self): self.press = Press(domain="sitetestpress.org") self.press.save() - self.journal_domain = 'fetesting.janeway.systems' - self.article_title = 'Test article: a test article' + self.journal_domain = "fetesting.janeway.systems" + self.article_title = "Test article: a test article" journal_kwargs = dict( code="fetests", domain=self.journal_domain, ) self.journal = make_test_journal(**journal_kwargs) - helpers.create_roles(['Author', 'Reviewer']) + helpers.create_roles(["Author", "Reviewer"]) self.author = helpers.create_user( - 'author@janeway.systems', - ['author'], + "author@janeway.systems", + ["author"], self.journal, - **{'first_name': 'New', 'last_name': 'Author'}, + **{"first_name": "New", "last_name": "Author"}, ) self.new_user = helpers.create_user( - 'new_user@janeway.systems', - ['author'], - self.journal, - **{'is_active': True} + "new_user@janeway.systems", ["author"], self.journal, **{"is_active": True} ) self.published_article = helpers.create_article( journal=self.journal, @@ -44,7 +40,7 @@ def setUp(self): abstract="test_abstract", date_published=timezone.now(), ) - keyword, c = submission_models.Keyword.objects.get_or_create(word='Test') + keyword, c = submission_models.Keyword.objects.get_or_create(word="Test") self.published_article.keywords.add(keyword) self.published_article.authors.add( self.author, @@ -54,36 +50,33 @@ def setUp(self): # create two issue types for testing issue_type = models.IssueType.objects.create( journal=self.journal, - code='issue', + code="issue", ) collection_type = models.IssueType.objects.create( journal=self.journal, - code='collection', + code="collection", ) self.issue, c = models.Issue.objects.get_or_create( journal=self.journal, volume="4", issue="4", defaults={ - 'date': timezone.now(), - 'issue_type': issue_type, - } + "date": timezone.now(), + "issue_type": issue_type, + }, ) self.issue.articles.add(self.published_article) self.collection, c = models.Issue.objects.get_or_create( journal=self.journal, - volume='0', - issue='0', + volume="0", + issue="0", issue_type=collection_type, - defaults={ - 'date': timezone.now(), - 'issue_title': 'Test Collection' - } + defaults={"date": timezone.now(), "issue_title": "Test Collection"}, ) self.collection.articles.add(self.published_article) self.editorial_group, c = core_models.EditorialGroup.objects.get_or_create( - name='Test Editor Group', + name="Test Editor Group", journal=self.journal, press=self.press, sequence=1, @@ -97,14 +90,14 @@ def setUp(self): def test_flat_front_end_pages(self): flat_pages_to_test = [ - 'website_index', - 'journal_articles', - 'search', - 'journal_submissions', - 'editorial_team', - 'contact', - 'journal_issues', - 'journal_collections_type', + "website_index", + "journal_articles", + "search", + "journal_submissions", + "editorial_team", + "contact", + "journal_issues", + "journal_collections_type", ] for page in flat_pages_to_test: @@ -114,25 +107,19 @@ def test_flat_front_end_pages(self): def test_issue_page(self): response = self.client.get( reverse( - 'journal_issue', + "journal_issue", kwargs={ - 'issue_id': self.issue.pk, - } + "issue_id": self.issue.pk, + }, ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, 200, ) - self.assertContains( - response, - 'Volume 4' - ) - self.assertContains( - response, - self.article_title - ) + self.assertContains(response, "Volume 4") + self.assertContains(response, self.article_title) def test_become_reviewer_page(self): self.client.force_login( @@ -140,9 +127,9 @@ def test_become_reviewer_page(self): ) response = self.client.get( reverse( - 'become_reviewer', + "become_reviewer", ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, @@ -150,57 +137,46 @@ def test_become_reviewer_page(self): ) self.client.post( reverse( - 'become_reviewer', + "become_reviewer", ), data={ - 'action': 'go', + "action": "go", }, - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) response = self.client.get( reverse( - 'become_reviewer', + "become_reviewer", ), - SERVER_NAME=self.journal_domain - ) - self.assertEqual( - response.status_code, - 200 - ) - self.assertTrue( - self.new_user.check_role(self.journal, 'reviewer') + SERVER_NAME=self.journal_domain, ) + self.assertEqual(response.status_code, 200) + self.assertTrue(self.new_user.check_role(self.journal, "reviewer")) def test_collection_page(self): response = self.client.get( reverse( - 'journal_collection', + "journal_collection", kwargs={ - 'collection_id': self.collection.pk, - } + "collection_id": self.collection.pk, + }, ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, 200, ) - self.assertContains( - response, - 'Test Collection' - ) - self.assertContains( - response, - self.article_title - ) + self.assertContains(response, "Test Collection") + self.assertContains(response, self.article_title) def test_editorial_team_group_page(self): response = self.client.get( reverse( - 'editorial_team_group', + "editorial_team_group", kwargs={ - 'group_id': self.editorial_group.pk, - } + "group_id": self.editorial_group.pk, + }, ), SERVER_NAME=self.journal_domain, ) @@ -208,17 +184,14 @@ def test_editorial_team_group_page(self): response.status_code, 200, ) - self.assertContains( - response, - 'New Author' - ) + self.assertContains(response, "New Author") def test_authors_page(self): response = self.client.get( reverse( - 'authors', + "authors", ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, @@ -227,16 +200,16 @@ def test_authors_page(self): def test_keywords_page(self): setting_handler.save_setting( - 'general', - 'keyword_list_page', + "general", + "keyword_list_page", self.journal, value=True, ) response = self.client.get( reverse( - 'keywords', + "keywords", ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, @@ -244,19 +217,19 @@ def test_keywords_page(self): ) self.assertContains( response, - 'Test', + "Test", ) def test_article_page(self): response = self.client.get( reverse( - 'article_view', + "article_view", kwargs={ - 'identifier_type': 'id', - 'identifier': self.published_article.pk, - } + "identifier_type": "id", + "identifier": self.published_article.pk, + }, ), - SERVER_NAME=self.journal_domain + SERVER_NAME=self.journal_domain, ) self.assertEqual( response.status_code, @@ -268,38 +241,26 @@ def test_article_page(self): ) def test_search_includes_article(self): - url = '{}?article_search=Test&sort=relevance'.format( + url = "{}?article_search=Test&sort=relevance".format( reverse( - 'search', + "search", ) ) - response = self.client.get( - url, - SERVER_NAME=self.journal_domain - ) - self.assertEqual( - response.status_code, - 200 - ) + response = self.client.get(url, SERVER_NAME=self.journal_domain) + self.assertEqual(response.status_code, 200) self.assertContains( response, self.article_title, ) def test_search_excludes_artucle(self): - url = '{}?article_search=Janeway&sort=relevance'.format( + url = "{}?article_search=Janeway&sort=relevance".format( reverse( - 'search', + "search", ) ) - response = self.client.get( - url, - SERVER_NAME=self.journal_domain - ) - self.assertEqual( - response.status_code, - 200 - ) + response = self.client.get(url, SERVER_NAME=self.journal_domain) + self.assertEqual(response.status_code, 200) self.assertNotContains( response, self.article_title, diff --git a/src/journal/tests/test_models.py b/src/journal/tests/test_models.py index 33c9269845..eab7114b48 100644 --- a/src/journal/tests/test_models.py +++ b/src/journal/tests/test_models.py @@ -10,7 +10,6 @@ class TestJournalSite(TestCase): - def setUp(self): self.request_factory = RequestFactory() self.press = Press(domain="sitetestpress.org") @@ -25,7 +24,8 @@ def setUp(self): @override_settings(URL_CONFIG="path") def test_path_mode_site_url_for_journal_in_context(self): request = self.request_factory.get( - "/test/banana/", SERVER_NAME="sitetestpress.org") + "/test/banana/", SERVER_NAME="sitetestpress.org" + ) request.journal = self.journal request.press = self.press @@ -39,7 +39,8 @@ def test_path_mode_site_url_for_journal_in_context(self): @override_settings(URL_CONFIG="path") def test_path_mode_site_url_for_journal_in_context_with_path(self): response = self.client.get( - "/modeltests/banana", SERVER_NAME="sitetestpress.org") + "/modeltests/banana", SERVER_NAME="sitetestpress.org" + ) path = reverse("website_index") result = self.journal.site_url(path) @@ -53,21 +54,19 @@ def test_path_mode_site_url_for_other_site(self): code="ojwithpath", domain="ojwithpath.org", ) - response = self.client.get( - "/ojwithpath/banana", SERVER_NAME="sitetest.org") + response = self.client.get("/ojwithpath/banana", SERVER_NAME="sitetest.org") result = self.journal.site_url() expected = "http://{}/{}".format( - self.press.domain, - self.journal.code, + self.press.domain, + self.journal.code, ) self.assertEqual(expected, result) @override_settings(URL_CONFIG="domain") def test_domain_mode_site_url_for_journal_in_context(self): - response = self.client.get( - "/modeltests/banana", SERVER_NAME="sitetest.org") + response = self.client.get("/modeltests/banana", SERVER_NAME="sitetest.org") result = self.journal.site_url() expected = "http://" + self.journal.domain @@ -77,7 +76,8 @@ def test_domain_mode_site_url_for_journal_in_context(self): @override_settings(URL_CONFIG="domain") def test_domain_mode_site_url_for_journal_in_context_with_path(self): response = self.client.get( - "/modeltests/banana", SERVER_NAME="sitetestpress.org") + "/modeltests/banana", SERVER_NAME="sitetestpress.org" + ) path = reverse("website_index") result = self.journal.site_url(path) @@ -91,8 +91,7 @@ def test_domain_mode_site_url_for_other_site(self): code="ojwithdomain", domain="ojwithdomain.org", ) - response = self.client.get( - "/banana", SERVER_NAME="ojwithdomain.org") + response = self.client.get("/banana", SERVER_NAME="ojwithdomain.org") result = self.journal.site_url() expected = "http://" + self.journal.domain @@ -106,7 +105,7 @@ def test_domain_mode_site_url_while_browsing_path_mode(self): code=journal_code, domain="ojwithdomain.org", ) - path = f'/{journal_code}/issues/' + path = f"/{journal_code}/issues/" result = journal.site_url(path=path) expected = f"http://{journal.domain}/issues/" @@ -136,7 +135,7 @@ def test_collection_display_title(self): issue.save() # Reload issue issue = models.Issue.objects.get(id=issue.id) - expected = ("Test Issue from Utils Testing Helpers") + expected = "Test Issue from Utils Testing Helpers" self.assertEqual(issue.display_title, expected) def test_issue_display_title_changed(self): @@ -156,7 +155,6 @@ def test_journal_settings_for_display_title_changed(self): # Reload issue issue = models.Issue.objects.get(id=issue.id) expected = ( - "Volume 1 • 2022 •" - " Test Issue from Utils Testing Helpers" + "Volume 1 • 2022 •" " Test Issue from Utils Testing Helpers" ) self.assertEqual(issue.display_title, expected) diff --git a/src/journal/tests/test_views.py b/src/journal/tests/test_views.py index c4ec82f067..9d34995257 100644 --- a/src/journal/tests/test_views.py +++ b/src/journal/tests/test_views.py @@ -8,8 +8,8 @@ from utils.testing import helpers -class PublishedArticlesListViewTests(TestCase): +class PublishedArticlesListViewTests(TestCase): @classmethod def setUpTestData(cls): cls.press = helpers.create_press() @@ -17,7 +17,7 @@ def setUpTestData(cls): cls.sections = [] cls.articles = [] thirty_days_ago = timezone.now() - timezone.timedelta(days=30) - for section_name in ['Article', 'Review', 'Comment', 'Editorial']: + for section_name in ["Article", "Review", "Comment", "Editorial"]: section = helpers.create_section( journal=cls.journal_one, name=section_name, @@ -27,9 +27,9 @@ def setUpTestData(cls): cls.articles.append( helpers.create_article( journal=cls.journal_one, - title=f'{section_name} {num}', + title=f"{section_name} {num}", section=section, - stage='Published', + stage="Published", date_published=thirty_days_ago, ) ) @@ -39,32 +39,30 @@ def setUp(self): def test_count_no_filters(self): data = {} - response = self.client.get('/articles/', data) + response = self.client.get("/articles/", data) self.assertIn( - f'60 results', + f"60 results", response.content.decode(), ) def test_count_filtered_on_section(self): - data = { - 'section__pk': self.sections[0].pk - } - response = self.client.get('/articles/', data) + data = {"section__pk": self.sections[0].pk} + response = self.client.get("/articles/", data) self.assertIn( - f'15 results', + f"15 results", response.content.decode(), ) def test_counts_match_with_filters(self): data = { - 'section__pk': self.sections[0].pk, + "section__pk": self.sections[0].pk, } - response = self.client.get('/articles/', data) + response = self.client.get("/articles/", data) self.assertIn( - f'15 results', + f"15 results", response.content.decode(), ) self.assertIn( - f'Article (15)', + f"Article (15)", response.content.decode(), ) diff --git a/src/journal/translation.py b/src/journal/translation.py index 1233d2959b..0cb3e8244d 100644 --- a/src/journal/translation.py +++ b/src/journal/translation.py @@ -5,9 +5,9 @@ @register(models.Journal) class JournalTranslationOptions(TranslationOptions): - fields = ('contact_info',) + fields = ("contact_info",) @register(models.Issue) class IssueTranslationOptions(TranslationOptions): - fields = ('cached_display_title',) + fields = ("cached_display_title",) diff --git a/src/journal/urls.py b/src/journal/urls.py index 4502f7dfea..22bf37a77f 100755 --- a/src/journal/urls.py +++ b/src/journal/urls.py @@ -16,231 +16,332 @@ urlpatterns = [ # Figures and download patterns - re_path(r'^article/(?P{0})/(?P[\w.-]+)/print/$' - ''.format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), + re_path( + r"^article/(?P{0})/(?P[\w.-]+)/print/$" "".format( + NON_DOI_PIPE_SEPARATED_IDENTIFIERS + ), views.print_article, - name='article_print_article'), - re_path(r'^article/(?P\d+)/galley/(?P\d+)/figure/(?P.*)/$', + name="article_print_article", + ), + re_path( + r"^article/(?P\d+)/galley/(?P\d+)/figure/(?P.*)/$", views.article_figure, - name='article_galley_figure'), - re_path(r'^article/(?Pid)/(?P.+)/file/(?P\d+)/replace$', + name="article_galley_figure", + ), + re_path( + r"^article/(?Pid)/(?P.+)/file/(?P\d+)/replace$", views.replace_article_file, - name='article_file_replace'), - re_path(r'^article/(?Pid)/(?P.+)/file/(?P\d+|None)/$', + name="article_file_replace", + ), + re_path( + r"^article/(?Pid)/(?P.+)/file/(?P\d+|None)/$", views.serve_article_file, - name='article_file_download'), - re_path(r'^article/(?Pid)/(?P.+)/file_history/(?P\d+|None)/$', + name="article_file_download", + ), + re_path( + r"^article/(?Pid)/(?P.+)/file_history/(?P\d+|None)/$", views.serve_article_file_history, - name='article_file_history_download'), - re_path(r'^article/(?P\d+)/galley/(?P\d+)/download/', + name="article_file_history_download", + ), + re_path( + r"^article/(?P\d+)/galley/(?P\d+)/download/", views.download_galley, - name='article_download_galley'), - re_path(r'^article/(?P\d+)/galley/(?P\d+)/view/', + name="article_download_galley", + ), + re_path( + r"^article/(?P\d+)/galley/(?P\d+)/view/", views.view_galley, - name='article_view_galley'), - re_path(r'^article/(?Pid)/(?P.+)/download/pdf/$', + name="article_view_galley", + ), + re_path( + r"^article/(?Pid)/(?P.+)/download/pdf/$", views.serve_article_pdf, - name='serve_article_pdf'), - re_path(r'^article/(?Pid)/(?P.+)/download/xml/$', + name="serve_article_pdf", + ), + re_path( + r"^article/(?Pid)/(?P.+)/download/xml/$", views.serve_article_xml, - name='serve_article_xml'), - re_path(r'^article/(?Pid)/(?P.+)/download/ris/$', + name="serve_article_xml", + ), + re_path( + r"^article/(?Pid)/(?P.+)/download/ris/$", views.serve_article_ris, - name='serve_article_ris'), - re_path(r'^article/(?Pid)/(?P.+)/download/bib/$', + name="serve_article_ris", + ), + re_path( + r"^article/(?Pid)/(?P.+)/download/bib/$", views.serve_article_bib, - name='serve_article_bib'), - re_path(r'^article/(?P{0})/(?P[\w.-]+)/table/(?P.+)$' - ''.format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), + name="serve_article_bib", + ), + re_path( + r"^article/(?P{0})/(?P[\w.-]+)/table/(?P.+)$" + "".format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), views.download_table, - name='article_table'), - re_path(r'^article/(?P{0})/(?P[\w.-]+)/(?P.+)$' - ''.format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), + name="article_table", + ), + re_path( + r"^article/(?P{0})/(?P[\w.-]+)/(?P.+)$" + "".format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), views.identifier_figure, - name='article_figure'), - + name="article_figure", + ), re_path( - r'^articles/$', + r"^articles/$", views.PublishedArticlesListView.as_view(), - name='journal_articles', + name="journal_articles", ), - re_path( - r'^funder_articles/(?P.+)$', + r"^funder_articles/(?P.+)$", views.funder_articles, - name='funder_articles', + name="funder_articles", ), - # Issues/Collections - re_path(r'^issues/$', views.issues, name='journal_issues'), - re_path(r'^issue/current/$', views.current_issue, name='current_issue'), - re_path(r'^issue/(?P\d+)/info/$', views.issue, name='journal_issue'), - re_path(r'^issue/(?P\d+)/download/(?P\d+)$', + re_path(r"^issues/$", views.issues, name="journal_issues"), + re_path(r"^issue/current/$", views.current_issue, name="current_issue"), + re_path(r"^issue/(?P\d+)/info/$", views.issue, name="journal_issue"), + re_path( + r"^issue/(?P\d+)/download/(?P\d+)$", views.download_issue_galley, - name='journal_issue_download_galley'), - re_path(r'^collections/$', views.collections, name='journal_collections_type'), - re_path(r'^collections/(?P\d+)/$', views.collection, name='journal_collection'), - + name="journal_issue_download_galley", + ), + re_path(r"^collections/$", views.collections, name="journal_collections_type"), + re_path( + r"^collections/(?P\d+)/$", + views.collection, + name="journal_collection", + ), # The URLS below are roughly equivalent but we need both because of backwards compatibility reasons - re_path(r'^collections/(?P[a-zA-Z-_]+)/$', views.collections, name='journal_collections'), - re_path(r'^collections/type/(?P[\da-zA-Z-_]+)/$', views.collections, name='journal_collections_with_digits'), + re_path( + r"^collections/(?P[a-zA-Z-_]+)/$", + views.collections, + name="journal_collections", + ), + re_path( + r"^collections/type/(?P[\da-zA-Z-_]+)/$", + views.collections, + name="journal_collections_with_digits", + ), # The URLS below are roughly equivalent but we need both because of backwards compatibility reasons - re_path(r'^collection/(?P[a-zA-Z-_]+)/$', views.collection_by_code, name='journal_collection_by_code'), - re_path(r'^collection/code/(?P[\da-zA-Z-_]+)/$', views.collection_by_code, name='journal_collection_by_code_with_digits'), - - re_path(r'^cover/$', views.serve_journal_cover, name='journal_cover_download'), - re_path(r'^volume/(?P\d+)/issue/(?P\d+)/$', views.volume, name='journal_volume'), - + re_path( + r"^collection/(?P[a-zA-Z-_]+)/$", + views.collection_by_code, + name="journal_collection_by_code", + ), + re_path( + r"^collection/code/(?P[\da-zA-Z-_]+)/$", + views.collection_by_code, + name="journal_collection_by_code_with_digits", + ), + re_path(r"^cover/$", views.serve_journal_cover, name="journal_cover_download"), + re_path( + r"^volume/(?P\d+)/issue/(?P\d+)/$", + views.volume, + name="journal_volume", + ), # Article patterns - re_path(r'^article/(?P{0})/(?P[\w.-]+)/edit/$' - ''.format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), + re_path( + r"^article/(?P{0})/(?P[\w.-]+)/edit/$" "".format( + NON_DOI_PIPE_SEPARATED_IDENTIFIERS + ), views.edit_article, - name='article_edit'), - re_path(r'^article/(?P{0})/(?P[\w.-]+)/$' - ''.format(NON_DOI_PIPE_SEPARATED_IDENTIFIERS), + name="article_edit", + ), + re_path( + r"^article/(?P{0})/(?P[\w.-]+)/$" "".format( + NON_DOI_PIPE_SEPARATED_IDENTIFIERS + ), views.article, - name='article_view' + name="article_view", + ), + re_path( + r"^article/(?Pdoi)/(?P{0})/$" "".format( + DOI_REGEX_PATTERN ), - re_path(r'^article/(?Pdoi)/(?P{0})/$' - ''.format(DOI_REGEX_PATTERN), views.doi_redirect, - name='doi_redirect'), - re_path(r'^article/(?P[\w.-_]+)/(?P[\w.-]+)/$', + name="doi_redirect", + ), + re_path( + r"^article/(?P[\w.-_]+)/(?P[\w.-]+)/$", views.article_from_identifier, - name='article_view_custom_identifier', - ), - - + name="article_view_custom_identifier", + ), # File management - re_path(r'^(?P\d+)/files/management/$', views.document_management, - name='document_management'), - re_path(r'^(?P\d+)/files/(?P\d+)/info/$', views.submit_files_info, - name='submit_replacement_files_info'), - re_path(r'^(?P\d+)/files/(?P\d+)/history/$', views.file_history, - name='file_history'), - re_path(r'^(?P\d+)/files/(?P\d+)/delete/$', views.file_delete, - name='file_delete'), - re_path(r'^(?P\d+)/files/(?P\d+)/old/(?P\d+)/reinstate/$', + re_path( + r"^(?P\d+)/files/management/$", + views.document_management, + name="document_management", + ), + re_path( + r"^(?P\d+)/files/(?P\d+)/info/$", + views.submit_files_info, + name="submit_replacement_files_info", + ), + re_path( + r"^(?P\d+)/files/(?P\d+)/history/$", + views.file_history, + name="file_history", + ), + re_path( + r"^(?P\d+)/files/(?P\d+)/delete/$", + views.file_delete, + name="file_delete", + ), + re_path( + r"^(?P\d+)/files/(?P\d+)/old/(?P\d+)/reinstate/$", views.file_reinstate, - name='file_reinstate'), - re_path(r'^(?P\d+)/file/(?P\d+)/makegalley/$', views.article_file_make_galley, - name='article_file_make_galley'), - re_path(r'^note/(?P\d+)/new/$', views.new_note, name='article_new_note'), - + name="file_reinstate", + ), + re_path( + r"^(?P\d+)/file/(?P\d+)/makegalley/$", + views.article_file_make_galley, + name="article_file_make_galley", + ), + re_path( + r"^note/(?P\d+)/new/$", views.new_note, name="article_new_note" + ), # Publication - re_path(r'^publish/$', - views.publish, name='publish'), - re_path(r'^publish/article/(?P\d+)/$', - views.publish_article, name='publish_article'), - re_path(r'^publish/article/(?P\d+)/check/$', - views.publish_article_check, name='publish_article_check'), - + re_path(r"^publish/$", views.publish, name="publish"), + re_path( + r"^publish/article/(?P\d+)/$", + views.publish_article, + name="publish_article", + ), + re_path( + r"^publish/article/(?P\d+)/check/$", + views.publish_article_check, + name="publish_article_check", + ), # Issues - re_path(r'^manage/issues/$', - views.manage_issues, name='manage_issues'), - re_path(r'^manage/issues/display/$', - views.manage_issue_display, name='manage_issue_display'), - re_path(r'^manage/issues/order/$', - views.issue_order, name='issue_order'), - re_path(r'^manage/issues/(?P\d+)/$', - views.manage_issues, name='manage_issues_id'), - re_path(r'^manage/issues/(?P\d+)/add/article/$', - views.issue_add_article, name='issue_add_article'), - re_path(r'^manage/issues/(?P\d+)/galley/$', - views.issue_galley, name='issue_galley'), - re_path(r'^manage/issues/(?P\d+)/order/$', - views.issue_article_order, name='issue_article_order'), - re_path(r'^manage/issues/(?P\d+)/editors/$', - views.add_guest_editor, name='manage_add_guest_editor'), - re_path(r'^manage/issues/(?P\d+)/editors/remove/$', - views.remove_issue_editor, name='manage_remove_issue_editor'), - re_path(r'^manage/issues/(?P\d+)/(?P[-\w.]+)/$', - views.manage_issues, name='manage_issues_event'), - re_path(r'^manage/issues/(?P\d+)/sort/sections/$', - views.sort_issue_sections, name='manage_sort_issue_sections'), - + re_path(r"^manage/issues/$", views.manage_issues, name="manage_issues"), + re_path( + r"^manage/issues/display/$", + views.manage_issue_display, + name="manage_issue_display", + ), + re_path(r"^manage/issues/order/$", views.issue_order, name="issue_order"), + re_path( + r"^manage/issues/(?P\d+)/$", + views.manage_issues, + name="manage_issues_id", + ), + re_path( + r"^manage/issues/(?P\d+)/add/article/$", + views.issue_add_article, + name="issue_add_article", + ), + re_path( + r"^manage/issues/(?P\d+)/galley/$", + views.issue_galley, + name="issue_galley", + ), + re_path( + r"^manage/issues/(?P\d+)/order/$", + views.issue_article_order, + name="issue_article_order", + ), + re_path( + r"^manage/issues/(?P\d+)/editors/$", + views.add_guest_editor, + name="manage_add_guest_editor", + ), + re_path( + r"^manage/issues/(?P\d+)/editors/remove/$", + views.remove_issue_editor, + name="manage_remove_issue_editor", + ), + re_path( + r"^manage/issues/(?P\d+)/(?P[-\w.]+)/$", + views.manage_issues, + name="manage_issues_event", + ), + re_path( + r"^manage/issues/(?P\d+)/sort/sections/$", + views.sort_issue_sections, + name="manage_sort_issue_sections", + ), # Article Archive re_path( - r'^manage/archive/$', + r"^manage/archive/$", views.published_article_archive, - name='manage_archive', + name="manage_archive", ), re_path( - r'^manage/archive/rejected-archived/$', + r"^manage/archive/rejected-archived/$", views.rejected_archived_article_archive, - name='manage_rejected_archived_archive', + name="manage_rejected_archived_archive", + ), + re_path( + r"^manage/archive/article/(?P\d+)/$", + views.manage_archive_article, + name="manage_archive_article", + ), + re_path( + r"^manage/article/(?P\d+)/log/$", + views.manage_article_log, + name="manage_article_log", + ), + re_path( + r"^manage/article/(?P\d+)/log/(?P\d+)/resend/$", + views.resend_logged_email, + name="manage_resend_logged_email", + ), + re_path( + r"^manage/articles/schedule/$", + views.publication_schedule, + name="publication_schedule", ), - re_path(r'^manage/archive/article/(?P\d+)/$', - views.manage_archive_article, name='manage_archive_article'), - - re_path(r'^manage/article/(?P\d+)/log/$', - views.manage_article_log, name='manage_article_log'), - - re_path(r'^manage/article/(?P\d+)/log/(?P\d+)/resend/$', - views.resend_logged_email, name='manage_resend_logged_email'), - - re_path(r'^manage/articles/schedule/$', - views.publication_schedule, name='publication_schedule'), - # Languages - re_path(r'^manage/languages/$', - views.manage_languages, name='manage_languages'), - + re_path(r"^manage/languages/$", views.manage_languages, name="manage_languages"), # Reviewer - re_path(r'^reviewer/$', - views.become_reviewer, name='become_reviewer'), + re_path(r"^reviewer/$", views.become_reviewer, name="become_reviewer"), # Contact - re_path(r'^contact/$', - views.contact, name='contact'), + re_path(r"^contact/$", views.contact, name="contact"), # Editorial team - re_path(r'^editorialteam/$', - views.editorial_team, name='editorial_team'), + re_path(r"^editorialteam/$", views.editorial_team, name="editorial_team"), # Editorial team - re_path(r'^editorialteam/(?P\d+)/$', - views.editorial_team, name='editorial_team_group'), + re_path( + r"^editorialteam/(?P\d+)/$", + views.editorial_team, + name="editorial_team_group", + ), # Authors page - re_path(r'^authors/$', - views.author_list, name='authors'), - + re_path(r"^authors/$", views.author_list, name="authors"), # Search - re_path(r'^search/$', - views.search, name='search'), - re_path(r'^keywords/$', - views.keywords, name='keywords'), - - re_path(r'^keywords/(?P\d+)/$', - views.keyword, name='keyword'), - + re_path(r"^search/$", views.search, name="search"), + re_path(r"^keywords/$", views.keywords, name="keywords"), + re_path(r"^keywords/(?P\d+)/$", views.keyword, name="keyword"), # Submissions - re_path(r'^submissions/$', - views.submissions, name='journal_submissions'), - + re_path(r"^submissions/$", views.submissions, name="journal_submissions"), # Edit file with Texture - re_path(r'^texture/(?P\d+)/edit/$', - views.texture_edit, name='texture_edit'), - + re_path( + r"^texture/(?P\d+)/edit/$", views.texture_edit, name="texture_edit" + ), # Download supplementary file - re_path(r'^download/article/(?P\d+)/supp_file/(?P\d+)/', + re_path( + r"^download/article/(?P\d+)/supp_file/(?P\d+)/", views.download_supp_file, - name='article_download_supp_file'), - + name="article_download_supp_file", + ), # Backup DOI patterns, redirect to pubid/id url of article - re_path(r'^article/(?Pdoi)/(?P{0})/print/$' - ''.format(DOI_REGEX_PATTERN), + re_path( + r"^article/(?Pdoi)/(?P{0})/print/$" "".format( + DOI_REGEX_PATTERN + ), views.doi_redirect, - name='print_doi_redirect'), - - - re_path(r'^email/user/(?P\d+)/$', - views.send_user_email, name='send_user_email'), - re_path(r'^email/user/(?P\d+)/article/(?P\d+)/$', - views.send_user_email, name='send_user_email_article'), - + name="print_doi_redirect", + ), + re_path( + r"^email/user/(?P\d+)/$", views.send_user_email, name="send_user_email" + ), + re_path( + r"^email/user/(?P\d+)/article/(?P\d+)/$", + views.send_user_email, + name="send_user_email_article", + ), # Manage users re_path( - r'^user/all/$', + r"^user/all/$", views.JournalUsers.as_view(), - name='journal_users', + name="journal_users", ), - ] diff --git a/src/journal/views.py b/src/journal/views.py index 8b62c4176b..6b1fe4f2e7 100755 --- a/src/journal/views.py +++ b/src/journal/views.py @@ -69,7 +69,7 @@ @has_journal @decorators.frontend_enabled def home(request): - """ Renders a journal homepage. + """Renders a journal homepage. :param request: the request associated with this call :return: a rendered template of the journal homepage @@ -83,20 +83,20 @@ def home(request): request, ) - template = 'journal/index.html' + template = "journal/index.html" context = { - 'homepage_elements': homepage_elements, - 'issues': issues_objects, - 'sections': sections, + "homepage_elements": homepage_elements, + "issues": issues_objects, + "sections": sections, } # call all registered plugin block hooks to get relevant contexts - for hook in settings.PLUGIN_HOOKS.get('yield_homepage_element_context', []): - if hook.get('name') in homepage_element_names: + for hook in settings.PLUGIN_HOOKS.get("yield_homepage_element_context", []): + if hook.get("name") in homepage_element_names: try: - hook_module = plugin_loader.import_module(hook.get('module')) - function = getattr(hook_module, hook.get('function')) + hook_module = plugin_loader.import_module(hook.get("module")) + function = getattr(hook_module, hook.get("function")) element_context = function(request, homepage_elements) for k, v in element_context.items(): @@ -112,7 +112,7 @@ def home(request): @has_journal def serve_journal_cover(request): - """ Serves the cover image for this journal or, if not affiliated with a journal, serves the press logo. + """Serves the cover image for this journal or, if not affiliated with a journal, serves the press logo. :param request: the request associated with this call :return: a streaming response of the retrieved image file @@ -133,23 +133,23 @@ def serve_journal_cover(request): @has_journal def funder_articles(request, funder_id): - """ Deprecated. Renders the list of articles in the journal. + """Deprecated. Renders the list of articles in the journal. - :param request: the request associated with this call - :return: a rendered template of all articles - """ - raise DeprecationWarning( - 'This view is deprecated.' - ) + :param request: the request associated with this call + :return: a rendered template of all articles + """ + raise DeprecationWarning("This view is deprecated.") - if request.POST and 'clear' in request.POST: + if request.POST and "clear" in request.POST: return logic.unset_article_session_variables(request) sections = submission_models.Section.objects.filter( journal=request.journal, is_filterable=True, ) - page, show, filters, sort, redirect, active_filters = logic.handle_article_controls(request, sections) + page, show, filters, sort, redirect, active_filters = logic.handle_article_controls( + request, sections + ) if redirect: return redirect @@ -176,15 +176,15 @@ def funder_articles(request, funder_id): except EmptyPage: articles = paginator.page(paginator.num_pages) - template = 'journal/articles.html' + template = "journal/articles.html" context = { - 'articles': articles, - 'sections': sections, - 'filters': filters, - 'sort': sort, - 'show': show, - 'active_filters': active_filters, - 'search_form': forms.SearchForm(), + "articles": articles, + "sections": sections, + "filters": filters, + "sort": sort, + "show": show, + "active_filters": active_filters, + "search_form": forms.SearchForm(), } return render(request, template, context) @@ -199,31 +199,40 @@ def articles(request): :param request: the request associated with this call :return: a rendered template of all articles """ - if request.POST and 'clear' in request.POST: + if request.POST and "clear" in request.POST: return logic.unset_article_session_variables(request) sections = submission_models.Section.objects.filter( journal=request.journal, is_filterable=True, ) - page, show, filters, sort, redirect, active_filters = logic.handle_article_controls(request, sections) + page, show, filters, sort, redirect, active_filters = logic.handle_article_controls( + request, sections + ) if redirect: return redirect - pinned_articles = [pin.article for pin in models.PinnedArticle.objects.filter( - journal=request.journal)] + pinned_articles = [ + pin.article + for pin in models.PinnedArticle.objects.filter(journal=request.journal) + ] pinned_article_pks = [article.pk for article in pinned_articles] - article_objects = submission_models.Article.objects.filter( - journal=request.journal, - stage=submission_models.STAGE_PUBLISHED, - date_published__lte=timezone.now(), - section__pk__in=filters, - ).prefetch_related( - 'frozenauthor_set', - ).order_by(sort).exclude( - pk__in=pinned_article_pks, + article_objects = ( + submission_models.Article.objects.filter( + journal=request.journal, + stage=submission_models.STAGE_PUBLISHED, + date_published__lte=timezone.now(), + section__pk__in=filters, + ) + .prefetch_related( + "frozenauthor_set", + ) + .order_by(sort) + .exclude( + pk__in=pinned_article_pks, + ) ) paginator = Paginator(article_objects, show) @@ -235,16 +244,16 @@ def articles(request): except EmptyPage: articles = paginator.page(paginator.num_pages) - template = 'journal/articles.html' + template = "journal/articles.html" context = { - 'pinned_articles': pinned_articles, - 'articles': articles, - 'sections': sections, - 'filters': filters, - 'sort': sort, - 'show': show, - 'active_filters': active_filters, - 'search_form': forms.SearchForm(), + "pinned_articles": pinned_articles, + "articles": articles, + "sections": sections, + "filters": filters, + "sort": sort, + "show": show, + "active_filters": active_filters, + "search_form": forms.SearchForm(), } return render(request, template, context) @@ -252,7 +261,7 @@ def articles(request): @has_journal @decorators.frontend_enabled def issues(request): - """ Renders the list of issues in the journal. + """Renders the list of issues in the journal. :param request: the request associated with this call :return: a rendered template of all issues @@ -266,13 +275,13 @@ def issues(request): issue_type=issue_type, date__lte=timezone.now(), ) - template = 'journal/issues.html' + template = "journal/issues.html" context = { - 'issues': issue_objects, - 'issue_type': issue_type, + "issues": issue_objects, + "issue_type": issue_type, } if request.journal.display_issues_grouped_by_decade: - context['issues_by_decade'] = request.journal.issues_by_decade( + context["issues_by_decade"] = request.journal.issues_by_decade( issues_to_sort=issue_objects, ) return render(request, template, context) @@ -281,22 +290,28 @@ def issues(request): @has_journal @decorators.frontend_enabled def current_issue(request, show_sidebar=True): - """ Renders the current journal issue""" + """Renders the current journal issue""" issue_id = request.journal.current_issue_id if not issue_id: - latest_issue = models.Issue.objects.filter( - date=timezone.now(), - ).order_by("-date").values("id").first() + latest_issue = ( + models.Issue.objects.filter( + date=timezone.now(), + ) + .order_by("-date") + .values("id") + .first() + ) if latest_issue: issue_id = latest_issue.id if not issue_id: - return redirect(reverse('journal_issues')) + return redirect(reverse("journal_issues")) return issue(request, request.journal.current_issue_id, show_sidebar=show_sidebar) + @has_journal @decorators.frontend_enabled def volume(request, volume_number, issue_number): - """ Redirects to an issue from its issue/volume number combination""" + """Redirects to an issue from its issue/volume number combination""" issue = models.Issue.objects.filter( issue=issue_number, volume=volume_number, @@ -305,15 +320,14 @@ def volume(request, volume_number, issue_number): ).first() if issue: - return redirect(reverse( - 'journal_issue', kwargs={'issue_id': issue.pk} - )) + return redirect(reverse("journal_issue", kwargs={"issue_id": issue.pk})) raise Http404 + @has_journal @decorators.frontend_enabled def issue(request, issue_id, show_sidebar=True): - """ Renders a specific issue/collection in the journal. + """Renders a specific issue/collection in the journal. It also returns all the other issues/collections in the journal for building a navigation menu @@ -323,7 +337,7 @@ def issue(request, issue_id, show_sidebar=True): :return: a rendered template of this issue """ issue_object = get_object_or_404( - models.Issue.objects.prefetch_related('editors'), + models.Issue.objects.prefetch_related("editors"), pk=issue_id, journal=request.journal, date__lte=timezone.now(), @@ -352,14 +366,14 @@ def issue(request, issue_id, show_sidebar=True): issue=issue_object, ) - template = 'journal/issue.html' + template = "journal/issue.html" context = { - 'issue': issue_object, - 'issues': issue_objects, - 'structure': issue_object.structure, # for backwards compatibility - 'articles': articles, - 'editors': editors, - 'show_sidebar': show_sidebar, + "issue": issue_object, + "issues": issue_objects, + "structure": issue_object.structure, # for backwards compatibility + "articles": articles, + "editors": editors, + "show_sidebar": show_sidebar, } return render(request, template, context) @@ -387,10 +401,10 @@ def collections(request, issue_type_code="collection"): articles__isnull=True, ) - template = 'journal/collections.html' + template = "journal/collections.html" context = { - 'collections': collections, - 'issue_type': issue_type, + "collections": collections, + "issue_type": issue_type, } return render(request, template, context) @@ -425,19 +439,25 @@ def collection_by_code(request, collection_code): journal=request.journal, ) if issue.issue_type.code == "issue": - return redirect(reverse( - 'journal_issue', kwargs={'issue_id': issue.pk}, - )) - return redirect(reverse( - "journal_collection", kwargs={"collection_id": issue.pk}, - )) + return redirect( + reverse( + "journal_issue", + kwargs={"issue_id": issue.pk}, + ) + ) + return redirect( + reverse( + "journal_collection", + kwargs={"collection_id": issue.pk}, + ) + ) @decorators.frontend_enabled @article_exists @article_stage_accepted_or_later_required def article(request, identifier_type, identifier): - """ Renders an article. + """Renders an article. :param request: the request associated with this call :param identifier_type: the identifier type @@ -459,34 +479,31 @@ def article(request, identifier_type, identifier): if galley: content = galley.file_content(recover=True) else: - content = '' + content = "" tables_in_galley = logic.get_all_tables_from_html(content) store_article_access( - request, - article_object, - "view", - galley.type if galley else None) + request, article_object, "view", galley.type if galley else None + ) else: article_object.abstract = ( "

This is an accepted article with a DOI pre-assigned" " that is not yet published.

" ) + (article_object.abstract or "") - if request.journal.disable_html_downloads: # exclude any HTML galleys. galleys = galleys.exclude( - file__mime_type='text/html', + file__mime_type="text/html", ) - template = 'journal/article.html' + template = "journal/article.html" context = { - 'article': article_object, - 'galleys': galleys, - 'identifier_type': identifier_type, - 'identifier': identifier, - 'article_content': content, - 'tables_in_galley': tables_in_galley, + "article": article_object, + "galleys": galleys, + "identifier_type": identifier_type, + "identifier": identifier, + "article_content": content, + "tables_in_galley": tables_in_galley, } return render(request, template, context) @@ -497,7 +514,7 @@ def article_from_identifier(request, identifier_type, identifier): id_models.Identifier, id_type=identifier_type, identifier=identifier, - article__journal = request.journal + article__journal=request.journal, ) return redirect(identifier.article.url) @@ -506,7 +523,7 @@ def article_from_identifier(request, identifier_type, identifier): @article_exists @article_stage_accepted_or_later_required def print_article(request, identifier_type, identifier): - """ Renders an article. + """Renders an article. :param request: the request associated with this call :param identifier_type: the identifier type @@ -514,7 +531,8 @@ def print_article(request, identifier_type, identifier): :return: a rendered template of the article """ article_object = submission_models.Article.get_article( - request.journal, identifier_type, identifier) + request.journal, identifier_type, identifier + ) content = None galleys = article_object.galley_set.filter(public=True) @@ -526,11 +544,14 @@ def print_article(request, identifier_type, identifier): if galley: content = galley.file_content(recover=True) else: - content = '' + content = "" else: article_object.abstract = "This is an accepted article with a DOI pre-assigned that is not yet published." - if not article_object.large_image_file or article_object.large_image_file.uuid_filename == '': + if ( + not article_object.large_image_file + or article_object.large_image_file.uuid_filename == "" + ): article_object.large_image_file = core_models.File() # assign the default image with a hacky patch # TODO: this should be set to a journal-wide setting @@ -538,15 +559,16 @@ def print_article(request, identifier_type, identifier): article_object.large_image_file.is_remote = True store_article_access( - request, article_object, 'view', galley.type if galley else None) + request, article_object, "view", galley.type if galley else None + ) - template = 'journal/print.html' + template = "journal/print.html" context = { - 'article': article_object, - 'galleys': galleys, - 'identifier_type': identifier_type, - 'identifier': identifier, - 'article_content': content, + "article": article_object, + "galleys": galleys, + "identifier_type": identifier_type, + "identifier": identifier, + "article_content": content, } return render(request, template, context) @@ -563,9 +585,9 @@ def keywords(request): """ keywords = request.journal.article_keywords() - template = 'journal/keywords.html' + template = "journal/keywords.html" context = { - 'keywords': keywords, + "keywords": keywords, } return render(request, template, context) @@ -586,10 +608,10 @@ def keyword(request, keyword_id): keywords__pk=keyword.pk, ) - template = 'journal/keyword.html' + template = "journal/keyword.html" context = { - 'keyword': keyword, - 'articles': articles, + "keyword": keyword, + "articles": articles, } return render(request, template, context) @@ -599,7 +621,7 @@ def keyword(request, keyword_id): @has_journal @article_exists def edit_article(request, identifier_type, identifier): - """ Renders the page to edit an article. Note that security enforcement on this view is handled in the submission + """Renders the page to edit an article. Note that security enforcement on this view is handled in the submission views. All this function does is to redirect to the 'submit_info' view with any identifiers translated to a PK. :param request: the request associated with this call @@ -607,36 +629,40 @@ def edit_article(request, identifier_type, identifier): :param identifier: the identifier :return: a rendered template to edit the article """ - article_object = submission_models.Article.get_article(request.journal, identifier_type, identifier) + article_object = submission_models.Article.get_article( + request.journal, identifier_type, identifier + ) - return redirect(reverse('submit_info', kwargs={'article_id': article_object.pk})) + return redirect(reverse("submit_info", kwargs={"article_id": article_object.pk})) def download_galley(request, article_id, galley_id): - """ Serves a galley file for an article + """Serves a galley file for an article :param request: an HttpRequest object :param article_id: an Article object PK :param galley_id: an Galley object PK :return: a streaming response of the requested file or a 404. """ - article = get_object_or_404(submission_models.Article, - pk=article_id, - journal=request.journal, - date_published__lte=timezone.now(), - stage__in=submission_models.PUBLISHED_STAGES) + article = get_object_or_404( + submission_models.Article, + pk=article_id, + journal=request.journal, + date_published__lte=timezone.now(), + stage__in=submission_models.PUBLISHED_STAGES, + ) galley = get_object_or_404( core_models.Galley, pk=galley_id, public=True, ) - embed = request.GET.get('embed', False) + embed = request.GET.get("embed", False) store_article_access( request, article, - 'view' if embed else 'download', + "view" if embed else "download", galley_type=galley.type, ) return files.serve_file(request, galley.file, article, public=True) @@ -656,34 +682,25 @@ def view_galley(request, article_id, galley_id): pk=article_id, journal=request.journal, date_published__lte=timezone.now(), - stage__in=submission_models.PUBLISHED_STAGES + stage__in=submission_models.PUBLISHED_STAGES, ) galley = get_object_or_404( core_models.Galley, pk=galley_id, article=article_to_serve, - file__mime_type='application/pdf' + file__mime_type="application/pdf", ) - store_article_access( - request, - article_to_serve, - 'view', - galley_type=galley.type - ) + store_article_access(request, article_to_serve, "view", galley_type=galley.type) - return files.serve_pdf_galley_to_browser( - request, - galley.file, - article_to_serve - ) + return files.serve_pdf_galley_to_browser(request, galley.file, article_to_serve) @has_request @article_stage_accepted_or_later_or_staff_required @file_user_required def serve_article_file(request, identifier_type, identifier, file_id): - """ Serves an article file. + """Serves an article file. :param request: the request associated with this call :param identifier_type: the identifier type for the article @@ -692,7 +709,7 @@ def serve_article_file(request, identifier_type, identifier, file_id): :return: a streaming response of the requested file or 404 """ - if not request.journal and request.site_type.code == 'press': + if not request.journal and request.site_type.code == "press": article_object = submission_models.Article.get_press_article( request.press, identifier_type, @@ -718,13 +735,16 @@ def serve_article_file(request, identifier_type, identifier, file_id): # if we are here then the carousel is requesting an image for an article that doesn't exist # return a default image instead - return redirect(static('common/img/default_carousel/carousel1.png')) + return redirect(static("common/img/default_carousel/carousel1.png")) @has_request @file_user_required def serve_article_file_history( - request, identifier_type, identifier, file_id, + request, + identifier_type, + identifier, + file_id, ): filehistory = get_object_or_404(core_models.FileHistory, pk=file_id) # File History objects are not storing article ids, check parent's instead @@ -738,7 +758,7 @@ def serve_article_file_history( @article_exists @file_edit_user_required def replace_article_file(request, identifier_type, identifier, file_id): - """ Renders the page to replace an article file + """Renders the page to replace an article file :param request: the request associated with this call :param identifier_type: the identifier type for the article @@ -746,49 +766,55 @@ def replace_article_file(request, identifier_type, identifier, file_id): :param file_id: the file ID to replace :return: a rendered template to replace the file """ - article_to_replace = submission_models.Article.get_article(request.journal, identifier_type, identifier) + article_to_replace = submission_models.Article.get_article( + request.journal, identifier_type, identifier + ) file_to_replace = get_object_or_404(core_models.File, pk=file_id) error = None - if request.GET.get('delete', False): + if request.GET.get("delete", False): file_delete(request, article_to_replace.pk, file_to_replace.pk) - return redirect(reverse('submit_files', kwargs={'article_id': article_to_replace.id})) + return redirect( + reverse("submit_files", kwargs={"article_id": article_to_replace.id}) + ) if request.POST: - - if 'replacement' in request.POST and request.FILES: - uploaded_file = request.FILES.get('replacement-file') + if "replacement" in request.POST and request.FILES: + uploaded_file = request.FILES.get("replacement-file") files.overwrite_file( uploaded_file, file_to_replace, - ('articles', article_to_replace.pk), + ("articles", article_to_replace.pk), ) - elif not request.FILES and 'back' not in request.POST: + elif not request.FILES and "back" not in request.POST: messages.add_message( request, messages.WARNING, - _('No file uploaded'), + _("No file uploaded"), ) - url = '{url}?return={get}'.format( - url=reverse('article_file_replace', - kwargs={'identifier_type': 'id', - 'identifier': article_to_replace.pk, - 'file_id': file_to_replace.pk} - ), - get=request.GET.get('return', ''), + url = "{url}?return={get}".format( + url=reverse( + "article_file_replace", + kwargs={ + "identifier_type": "id", + "identifier": article_to_replace.pk, + "file_id": file_to_replace.pk, + }, + ), + get=request.GET.get("return", ""), ) return redirect(url) - return redirect(request.GET.get('return', reverse('core_dashboard'))) + return redirect(request.GET.get("return", reverse("core_dashboard"))) template = "journal/replace_file.html" context = { - 'article': article_to_replace, - 'old_file': file_to_replace, - 'error': error, + "article": article_to_replace, + "old_file": file_to_replace, + "error": error, } return render(request, template, context) @@ -798,7 +824,7 @@ def replace_article_file(request, identifier_type, identifier, file_id): @article_exists @file_edit_user_required def file_reinstate(request, article_id, file_id, file_history_id): - """ Replaces a file with an older version of itself + """Replaces a file with an older version of itself :param request: the request associated with this call :param article_id: the article on which to replace the file @@ -811,13 +837,13 @@ def file_reinstate(request, article_id, file_id, file_history_id): files.reinstate_historic_file(article, current_file, file_history) - return redirect(request.GET['return']) + return redirect(request.GET["return"]) @login_required @file_edit_user_required def submit_files_info(request, article_id, file_id): - """ Renders a template to submit information about a file. + """Renders a template to submit information about a file. :param request: the request associated with this call :param article_id: the ID of the associated article @@ -838,13 +864,13 @@ def submit_files_info(request, article_id, file_id): if form.is_valid(): form.save() # TODO: this needs a better redirect - return redirect(reverse('kanban')) + return redirect(reverse("kanban")) template = "review/submit_replacement_files_info.html" context = { - 'article': article_object, - 'file': file_object, - 'form': form, + "article": article_object, + "file": file_object, + "form": form, } return render(request, template, context) @@ -854,7 +880,7 @@ def submit_files_info(request, article_id, file_id): @file_history_user_required @editor_user_required_and_can_see_pii def file_history(request, article_id, file_id): - """ Renders a template to show the history of a file. + """Renders a template to show the history of a file. :param request: the request associated with this call :param article_id: the ID of the associated article @@ -863,7 +889,7 @@ def file_history(request, article_id, file_id): """ if request.POST: - return redirect(request.GET['return']) + return redirect(request.GET["return"]) article_object = get_object_or_404( submission_models.Article, @@ -874,8 +900,8 @@ def file_history(request, article_id, file_id): template = "journal/file_history.html" context = { - 'article': article_object, - 'file': file_object, + "article": article_object, + "file": file_object, } return render(request, template, context) @@ -883,9 +909,7 @@ def file_history(request, article_id, file_id): @editor_user_required def issue_file_history(request, issue_id): - """ Returns the file history of a given Issue Galley file - - """ + """Returns the file history of a given Issue Galley file""" # TODO: Combine with `file_history` above, disabled until GH #865 raise Http404 issue_galley = get_object_or_404(models.IssueGalley, issue__pk=issue_id) @@ -893,8 +917,8 @@ def issue_file_history(request, issue_id): template = "journal/file_history.html" context = { - 'article': None, - 'file': file_object, + "article": None, + "file": file_object, } return render(request, template, context) @@ -903,7 +927,7 @@ def issue_file_history(request, issue_id): @login_required @file_edit_user_required def file_delete(request, article_id, file_id): - """ Renders a template to delete a file. + """Renders a template to delete a file. :param request: the request associated with this call :param article_id: the ID of the associated articled @@ -919,13 +943,13 @@ def file_delete(request, article_id, file_id): file_object.delete() - return redirect(request.GET['return']) + return redirect(request.GET["return"]) @file_user_required @production_user_or_editor_required def article_file_make_galley(request, article_id, file_id): - """ Copies a file to be a publicly available galley + """Copies a file to be a publicly available galley :param request: the request associated with this call :param article_id: the ID of the associated articled @@ -942,16 +966,17 @@ def article_file_make_galley(request, article_id, file_id): content_file = ContentFile(blob) content_file.name = janeway_file.original_filename - # Avoid circular import. from production import logic as production_logic production_logic.save_galley( - article_object, request, content_file, + article_object, + request, + content_file, is_galley=True, ) - return redirect(request.GET['return']) + return redirect(request.GET["return"]) def identifier_figure(request, identifier_type, identifier, file_name): @@ -964,9 +989,7 @@ def identifier_figure(request, identifier_type, identifier, file_name): :return: a streaming file reponse """ figure_article = submission_models.Article.get_article( - request.journal, - identifier_type, - identifier + request.journal, identifier_type, identifier ) if not figure_article: @@ -981,11 +1004,13 @@ def identifier_figure(request, identifier_type, identifier, file_name): # Use a filter with .first() here to avoid an error when two images with # the same name are present. - figure = galley.images.filter( - original_filename=file_name - ).order_by( - '-last_modified', - ).first() + figure = ( + galley.images.filter(original_filename=file_name) + .order_by( + "-last_modified", + ) + .first() + ) if not figure: raise Http404 @@ -994,7 +1019,7 @@ def identifier_figure(request, identifier_type, identifier, file_name): def article_figure(request, article_id, galley_id, file_name): - """ Returns a galley article figure + """Returns a galley article figure :param request: an HttpRequest object :param article_id: an Article object PK @@ -1014,11 +1039,13 @@ def article_figure(request, article_id, galley_id, file_name): ) # Use a filter with .first() here to avoid an error when two images with # the same name are present. - figure = galley.images.filter( - original_filename=file_name - ).order_by( - '-last_modified', - ).first() + figure = ( + galley.images.filter(original_filename=file_name) + .order_by( + "-last_modified", + ) + .first() + ) if not figure: raise Http404 @@ -1038,9 +1065,9 @@ def publish(request): journal=request.journal, ) - template = 'journal/publish.html' + template = "journal/publish.html" context = { - 'articles': articles, + "articles": articles, } return render(request, template, context) @@ -1055,10 +1082,11 @@ def publish_article(request, article_id): :return: contextualised django template """ from submission import forms as submission_forms + article = get_object_or_404( submission_models.Article, - Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION) | - Q(stage=submission_models.STAGE_PUBLISHED), + Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION) + | Q(stage=submission_models.STAGE_PUBLISHED), pk=article_id, journal=request.journal, ) @@ -1069,10 +1097,10 @@ def publish_article(request, article_id): new_issue_form = issue_forms.NewIssue(journal=article.journal) pub_date_form = submission_forms.PubDateForm(instance=article) notification_form_kwargs = { - 'email_context': { - 'article': article, + "email_context": { + "article": article, }, - 'request': request, + "request": request, } notification_initial = logic.get_initial_for_prepub_notifications( request, @@ -1082,54 +1110,55 @@ def publish_article(request, article_id): form_kwargs=notification_form_kwargs, initial=notification_initial, ) - modal = request.GET.get('m', None) + modal = request.GET.get("m", None) if request.POST: - if 'assign_issue' in request.POST: + if "assign_issue" in request.POST: try: issue = models.Issue.objects.get( - pk=request.POST['assign_issue'], + pk=request.POST["assign_issue"], ) logic.handle_assign_issue(request, article, issue) except models.Issue.DoesNotExist: messages.add_message( - request, messages.WARNING, - _('Issue not in this journal’s issue list.') + request, + messages.WARNING, + _("Issue not in this journal’s issue list."), ) return redirect( - '{0}?m=issue'.format( + "{0}?m=issue".format( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) ) ) - if 'unassign_issue' in request.POST: + if "unassign_issue" in request.POST: logic.handle_unassign_issue(request, article, issues) return redirect( - '{0}?m=issue'.format( + "{0}?m=issue".format( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) ) ) - if 'new_issue' in request.POST: + if "new_issue" in request.POST: new_issue_form, modal, new_issue = logic.handle_new_issue(request) if new_issue: return redirect( - '{0}?m=issue'.format( + "{0}?m=issue".format( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) ) ) - if 'pubdate' in request.POST: + if "pubdate" in request.POST: pub_date_form = submission_forms.PubDateForm( request.POST, instance=article, @@ -1138,27 +1167,28 @@ def publish_article(request, article_id): article = pub_date_form.save() if article.date_published: messages.add_message( - request, messages.SUCCESS, + request, + messages.SUCCESS, _( f'Publication date set to { article.date_published.strftime("%Y-%m-%d %H:%M %Z") } ' f'({ naturaltime(article.date_published) })' - ) + ), ) else: messages.add_message( - request, messages.SUCCESS, - _('Publication date unset') + request, messages.SUCCESS, _("Publication date unset") ) else: messages.add_message( - request, messages.WARNING, + request, + messages.WARNING, _( - f'Something went wrong when trying to save the form. ' - f'Please try again.' - ) + f"Something went wrong when trying to save the form. " + f"Please try again." + ), ) - if 'notifications' in request.POST: + if "notifications" in request.POST: notification_formset = forms.PrepubNotificationFormSet( request.POST, form_kwargs=notification_form_kwargs, @@ -1174,39 +1204,39 @@ def publish_article(request, article_id): messages.add_message( request, messages.ERROR, - 'Something went wrong. Please try again.', + "Something went wrong. Please try again.", ) - if 'galley' in request.POST: + if "galley" in request.POST: logic.set_render_galley(request, article) return redirect( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) ) - if 'image' in request.POST or 'delete_image' in request.POST: + if "image" in request.POST or "delete_image" in request.POST: logic.set_article_image(request, article) shared.clear_cache() return redirect( "{0}{1}".format( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ), "?m=article_image", ) ) - if 'open_reviews' in request.POST: + if "open_reviews" in request.POST: logic.set_open_reviews(request, article) reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) - if 'publish' in request.POST: + if "publish" in request.POST: article.stage = submission_models.STAGE_PUBLISHED article.snapshot_authors(force_update=False) article.close_core_workflow_objects() @@ -1217,8 +1247,7 @@ def publish_article(request, article_id): article.save() # Fire publication event - kwargs = {'article': article, - 'request': request} + kwargs = {"article": article, "request": request} event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_PUBLISHED, task_object=article, @@ -1227,30 +1256,30 @@ def publish_article(request, article_id): # Attempt to register xref DOI for identifier in article.identifier_set.all(): - if identifier.id_type == 'doi': + if identifier.id_type == "doi": status, error = identifier.register() messages.add_message( - request, - messages.INFO if not error else messages.ERROR, - status + request, messages.INFO if not error else messages.ERROR, status ) messages.add_message( request, messages.SUCCESS, - _('Article set for publication.'), + _("Article set for publication."), ) # clear the cache shared.clear_cache() if request.journal.element_in_workflow( - element_name='prepublication', + element_name="prepublication", ): - workflow_kwargs = {'handshake_url': 'publish', - 'request': request, - 'article': article, - 'switch_stage': True} + workflow_kwargs = { + "handshake_url": "publish", + "request": request, + "article": article, + "switch_stage": True, + } return event_logic.Events.raise_event( event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, task_object=article, @@ -1259,21 +1288,21 @@ def publish_article(request, article_id): return redirect( reverse( - 'publish_article', - kwargs={'article_id': article.pk}, + "publish_article", + kwargs={"article_id": article.pk}, ) ) - template = 'journal/publish_article.html' + template = "journal/publish_article.html" context = { - 'article': article, - 'doi_data': doi_data, - 'doi': doi, - 'issues': issues, - 'new_issue_form': new_issue_form, - 'modal': modal, - 'pub_date_form': pub_date_form, - 'notification_formset': notification_formset, + "article": article, + "doi_data": doi_data, + "doi": doi, + "issues": issues, + "new_issue_form": new_issue_form, + "modal": modal, + "pub_date_form": pub_date_form, + "notification_formset": notification_formset, } return render(request, template, context) @@ -1290,34 +1319,44 @@ def publish_article_check(request, article_id): """ article = get_object_or_404( submission_models.Article, - Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION) | - Q(stage=submission_models.STAGE_PUBLISHED), + Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION) + | Q(stage=submission_models.STAGE_PUBLISHED), pk=article_id, journal=request.journal, ) - task_type = request.POST.get('task_type') - id = request.POST.get('id') - value = True if int(request.POST.get('value')) == 1 else False + task_type = request.POST.get("task_type") + id = request.POST.get("id") + value = True if int(request.POST.get("value")) == 1 else False if not task_type or not id: - return HttpResponse(json.dumps({'error': 'no data supplied'}), content_type="application/json") + return HttpResponse( + json.dumps({"error": "no data supplied"}), content_type="application/json" + ) - if task_type == 'fixed': + if task_type == "fixed": update_dict = {id: value} for k, v in update_dict.items(): setattr(article.fixedpubcheckitems, k, v) article.fixedpubcheckitems.save() - return HttpResponse(json.dumps({'action': 'ok', 'id': value}), content_type="application/json") + return HttpResponse( + json.dumps({"action": "ok", "id": value}), content_type="application/json" + ) else: - item_to_update = get_object_or_404(models.PrePublicationChecklistItem, pk=id, article=article) - item_to_update.completed = True if int(request.POST.get('value')) == 1 else False + item_to_update = get_object_or_404( + models.PrePublicationChecklistItem, pk=id, article=article + ) + item_to_update.completed = ( + True if int(request.POST.get("value")) == 1 else False + ) item_to_update.completed_by = request.user item_to_update.completed_on = timezone.now() item_to_update.save() - return HttpResponse(json.dumps({'action': 'ok', 'id': value}), content_type="application/json") + return HttpResponse( + json.dumps({"action": "ok", "id": value}), content_type="application/json" + ) @editor_user_required @@ -1330,27 +1369,36 @@ def manage_issues(request, issue_id=None, event=None): :return: HttpResponse object or HttpRedirect if POSTed """ from core.logic import resize_and_crop + issue_list = models.Issue.objects.filter(journal=request.journal) - issue, modal, form, galley_form, sort_form = None, None, issue_forms.NewIssue(journal=request.journal), None, None + issue, modal, form, galley_form, sort_form = ( + None, + None, + issue_forms.NewIssue(journal=request.journal), + None, + None, + ) if issue_id: issue = get_object_or_404(models.Issue, pk=issue_id) form = issue_forms.NewIssue(instance=issue, journal=issue.journal) galley_form = issue_forms.IssueGalleyForm() sort_form = issue_forms.SortForm() - if event == 'edit': - modal = 'issue' - if event == 'delete': - modal = 'deleteme' - if event == 'remove': - article_id = request.GET.get('article') - article = get_object_or_404(submission_models.Article, pk=article_id, pk__in=issue.article_pks) + if event == "edit": + modal = "issue" + if event == "delete": + modal = "deleteme" + if event == "remove": + article_id = request.GET.get("article") + article = get_object_or_404( + submission_models.Article, pk=article_id, pk__in=issue.article_pks + ) issue.articles.remove(article) - return redirect(reverse('manage_issues_id', kwargs={'issue_id': issue.pk})) + return redirect(reverse("manage_issues_id", kwargs={"issue_id": issue.pk})) if request.POST: - if 'make_current' in request.POST: - issue = models.Issue.objects.get(id=request.POST['make_current']) + if "make_current" in request.POST: + issue = models.Issue.objects.get(id=request.POST["make_current"]) if issue.is_published(): request.journal.current_issue = issue @@ -1359,32 +1407,36 @@ def manage_issues(request, issue_id=None, event=None): messages.add_message( request, messages.WARNING, - 'Issues that have a future publication date cannot be set as the current issue for a journal.', + "Issues that have a future publication date cannot be set as the current issue for a journal.", ) issue = None - return redirect(reverse('manage_issues')) + return redirect(reverse("manage_issues")) - if 'delete_issue' in request.POST: + if "delete_issue" in request.POST: issue.delete() - return redirect(reverse('manage_issues')) + return redirect(reverse("manage_issues")) - if 'sort' in request.POST: + if "sort" in request.POST: logic.sort_issues(request, issue_list) - return redirect(reverse('manage_issues')) + return redirect(reverse("manage_issues")) - if issue and 'sort_articles' in request.POST: + if issue and "sort_articles" in request.POST: sort_form = issue_forms.SortForm(request.POST) if sort_form.is_valid(): issue.order_articles_in_sections( - sort_field=sort_form.cleaned_data.get('sort_field'), - order=sort_form.cleaned_data.get('order'), + sort_field=sort_form.cleaned_data.get("sort_field"), + order=sort_form.cleaned_data.get("order"), ) - if 'save_issue' in request.POST: + if "save_issue" in request.POST: if issue: - form = issue_forms.NewIssue(request.POST, request.FILES, instance=issue, journal=request.journal) + form = issue_forms.NewIssue( + request.POST, request.FILES, instance=issue, journal=request.journal + ) else: - form = issue_forms.NewIssue(request.POST, request.FILES, journal=request.journal) + form = issue_forms.NewIssue( + request.POST, request.FILES, journal=request.journal + ) if form.is_valid(): save_issue = form.save(commit=False) @@ -1393,21 +1445,23 @@ def manage_issues(request, issue_id=None, event=None): if request.FILES and save_issue.large_image: resize_and_crop(save_issue.large_image.path, [750, 324]) if issue: - return redirect(reverse('manage_issues_id', kwargs={'issue_id': issue.pk})) + return redirect( + reverse("manage_issues_id", kwargs={"issue_id": issue.pk}) + ) else: - return redirect(reverse('manage_issues')) + return redirect(reverse("manage_issues")) else: - modal = 'issue' + modal = "issue" - template = 'journal/manage/issues.html' + template = "journal/manage/issues.html" context = { - 'issues': issue_list if not issue else [issue], - 'issue': issue, - 'form': form, - 'modal': modal, - 'galley_form': galley_form, - 'articles': issue.get_sorted_articles(published_only=False) if issue else None, - 'sort_form': sort_form, + "issues": issue_list if not issue else [issue], + "issue": issue, + "form": form, + "modal": modal, + "galley_form": galley_form, + "articles": issue.get_sorted_articles(published_only=False) if issue else None, + "sort_form": sort_form, } return render(request, template, context) @@ -1433,13 +1487,13 @@ def manage_issue_display(request): issue_display_form.save() return redirect( reverse( - 'manage_issue_display', + "manage_issue_display", ) ) - template = 'journal/manage/issue_display.html' + template = "journal/manage/issue_display.html" context = { - 'issue_display_form': issue_display_form, + "issue_display_form": issue_display_form, } return render(request, template, context) @@ -1449,9 +1503,9 @@ def manage_issue_display(request): def issue_galley(request, issue_id, delete=False): issue = get_object_or_404(models.Issue, pk=issue_id) - if request.method == 'POST': + if request.method == "POST": form = issue_forms.IssueGalleyForm(request.POST, request.FILES) - if 'delete' in request.POST: + if "delete" in request.POST: issue_galley = get_object_or_404(models.IssueGalley, issue=issue) issue_galley.delete() messages.info(request, "Issue Galley Deleted") @@ -1475,12 +1529,9 @@ def issue_galley(request, issue_id, delete=False): messages.info(request, "Issue Galley Uploaded") elif form.errors: - messages.error( - request, - "\n".join(field.errors.as_text() for field in form) - ) + messages.error(request, "\n".join(field.errors.as_text() for field in form)) - return redirect(reverse('manage_issues_id', kwargs={'issue_id': issue.pk})) + return redirect(reverse("manage_issues_id", kwargs={"issue_id": issue.pk})) @editor_user_required @@ -1489,20 +1540,26 @@ def sort_issue_sections(request, issue_id): sections = issue.all_sections if request.POST: - if 'up' in request.POST: - section_id = request.POST.get('up') - section_to_move_up = get_object_or_404(submission_models.Section, pk=section_id, journal=request.journal) + if "up" in request.POST: + section_id = request.POST.get("up") + section_to_move_up = get_object_or_404( + submission_models.Section, pk=section_id, journal=request.journal + ) if section_to_move_up != issue.first_section: section_to_move_up_index = sections.index(section_to_move_up) section_to_move_down = sections[section_to_move_up_index - 1] - section_to_move_up_ordering, c = models.SectionOrdering.objects.get_or_create( - issue=issue, - section=section_to_move_up) - section_to_move_down_ordering, c = models.SectionOrdering.objects.get_or_create( - issue=issue, - section=section_to_move_down) + section_to_move_up_ordering, c = ( + models.SectionOrdering.objects.get_or_create( + issue=issue, section=section_to_move_up + ) + ) + section_to_move_down_ordering, c = ( + models.SectionOrdering.objects.get_or_create( + issue=issue, section=section_to_move_down + ) + ) section_to_move_up_ordering.order = section_to_move_up_index - 1 section_to_move_down_ordering.order = section_to_move_up_index @@ -1513,11 +1570,11 @@ def sort_issue_sections(request, issue_id): messages.add_message( request, messages.WARNING, - _('You cannot move the first section up the order list'), + _("You cannot move the first section up the order list"), ) - elif 'down' in request.POST: - section_id = request.POST.get('down') + elif "down" in request.POST: + section_id = request.POST.get("down") section_to_move_down = get_object_or_404( submission_models.Section, pk=section_id, @@ -1528,12 +1585,16 @@ def sort_issue_sections(request, issue_id): section_to_move_down_index = sections.index(section_to_move_down) section_to_move_up = sections[section_to_move_down_index + 1] - section_to_move_up_ordering, c = models.SectionOrdering.objects.get_or_create( - issue=issue, - section=section_to_move_up) - section_to_move_down_ordering, c = models.SectionOrdering.objects.get_or_create( - issue=issue, - section=section_to_move_down) + section_to_move_up_ordering, c = ( + models.SectionOrdering.objects.get_or_create( + issue=issue, section=section_to_move_up + ) + ) + section_to_move_down_ordering, c = ( + models.SectionOrdering.objects.get_or_create( + issue=issue, section=section_to_move_down + ) + ) section_to_move_up_ordering.order = section_to_move_down_index section_to_move_down_ordering.order = section_to_move_down_index + 1 @@ -1545,16 +1606,16 @@ def sort_issue_sections(request, issue_id): messages.add_message( request, messages.WARNING, - _('You cannot move the last section down the order list'), + _("You cannot move the last section down the order list"), ) else: messages.add_message( request, messages.WARNING, - _('This page accepts post requests only.'), + _("This page accepts post requests only."), ) - return redirect(reverse('manage_issues_id', kwargs={'issue_id': issue.pk})) + return redirect(reverse("manage_issues_id", kwargs={"issue_id": issue.pk})) @editor_user_required @@ -1569,26 +1630,23 @@ def issue_add_article(request, issue_id): issue = get_object_or_404(models.Issue, pk=issue_id, journal=request.journal) articles = submission_models.Article.active_objects.filter( journal=request.journal, - ).exclude( - pk__in=issue.article_pks - ) + ).exclude(pk__in=issue.article_pks) - if request.POST.get('article'): - article_id = request.POST.get('article') + if request.POST.get("article"): + article_id = request.POST.get("article") article = get_object_or_404( - submission_models.Article, pk=article_id, journal=request.journal) + submission_models.Article, pk=article_id, journal=request.journal + ) added = logic.handle_assign_issue(request, article, issue) if added: - return redirect(reverse( - 'manage_issues_id', kwargs={'issue_id': issue.pk})) + return redirect(reverse("manage_issues_id", kwargs={"issue_id": issue.pk})) else: - return redirect(reverse( - 'issue_add_article', kwargs={'issue_id': issue.pk})) + return redirect(reverse("issue_add_article", kwargs={"issue_id": issue.pk})) - template = 'journal/manage/issue_add_article.html' + template = "journal/manage/issue_add_article.html" context = { - 'issue': issue, - 'articles': articles, + "issue": issue, + "articles": articles, } return render(request, template, context) @@ -1613,9 +1671,9 @@ def add_guest_editor(request, issue_id): editors = models.IssueEditor.objects.filter(issue=issue) if request.POST: - if 'user' in request.POST: - user_id = request.POST.get('user') - role = request.POST.get('role') + if "user" in request.POST: + user_id = request.POST.get("user") + role = request.POST.get("role") user = get_object_or_404(core_models.Account, pk=user_id) @@ -1623,13 +1681,13 @@ def add_guest_editor(request, issue_id): messages.add_message( request, messages.WARNING, - _('User is already a guest editor.'), + _("User is already a guest editor."), ) elif user not in users: messages.add_message( request, messages.WARNING, - _('This user is not a member of this journal.'), + _("This user is not a member of this journal."), ) else: models.IssueEditor.objects.create( @@ -1639,24 +1697,23 @@ def add_guest_editor(request, issue_id): ) return redirect( - reverse( - 'manage_add_guest_editor', - kwargs={'issue_id': issue.pk} - ) + reverse("manage_add_guest_editor", kwargs={"issue_id": issue.pk}) ) - elif 'guesteditors[]' in request.POST: - posted_guest_editor_pks = [int(pk) for pk in request.POST.getlist('guesteditors[]')] + elif "guesteditors[]" in request.POST: + posted_guest_editor_pks = [ + int(pk) for pk in request.POST.getlist("guesteditors[]") + ] shared.set_order( objects=editors, - order_attr_name='sequence', - pk_list=posted_guest_editor_pks + order_attr_name="sequence", + pk_list=posted_guest_editor_pks, ) - return HttpResponse('Guest Editor Sequence Updated.') - template = 'journal/manage/add_guest_editor.html' + return HttpResponse("Guest Editor Sequence Updated.") + template = "journal/manage/add_guest_editor.html" context = { - 'issue': issue, - 'users': users, - 'editors': editors, + "issue": issue, + "users": users, + "editors": editors, } return render(request, template, context) @@ -1671,8 +1728,8 @@ def remove_issue_editor(request, issue_id): journal=request.journal, ) - if 'user_remove' in request.POST: - issue_editor_id = request.POST.get('user_remove', 0) + if "user_remove" in request.POST: + issue_editor_id = request.POST.get("user_remove", 0) if issue_editor_id: try: @@ -1682,25 +1739,25 @@ def remove_issue_editor(request, issue_id): messages.add_message( request, messages.SUCCESS, - _('Editor removed from Issue.'), + _("Editor removed from Issue."), ) except models.IssueEditor.DoesNotExist: messages.add_message( request, messages.WARNING, - _('Issue Editor not found.'), + _("Issue Editor not found."), ) else: messages.add_message( request, messages.WARNING, - _('No Issue Editor ID supplied.'), + _("No Issue Editor ID supplied."), ) return redirect( reverse( - 'manage_add_guest_editor', - kwargs={'issue_id': issue.pk}, + "manage_add_guest_editor", + kwargs={"issue_id": issue.pk}, ) ) @@ -1717,14 +1774,14 @@ def issue_order(request): issues = models.Issue.objects.filter(journal=request.journal) if request.POST: - ids = [int(_id) for _id in request.POST.getlist('issues[]')] + ids = [int(_id) for _id in request.POST.getlist("issues[]")] for issue in issues: order = ids.index(issue.pk) issue.order = order issue.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @csrf_exempt @@ -1739,15 +1796,16 @@ def issue_article_order(request, issue_id=None): issue = get_object_or_404(models.Issue, pk=issue_id, journal=request.journal) if request.POST: - ids = request.POST.getlist('articles[]') + ids = request.POST.getlist("articles[]") ids = [int(_id) for _id in ids] articles = submission_models.Article.objects.filter( - id__in=ids, journal=request.journal) + id__in=ids, journal=request.journal + ) section = None - for order, article in enumerate(sorted( - articles, key=lambda x: ids.index(x.pk) - )): + for order, article in enumerate( + sorted(articles, key=lambda x: ids.index(x.pk)) + ): section = article.section if not issue.articles.filter(id=article.id).exists(): logger.error( @@ -1757,20 +1815,19 @@ def issue_article_order(request, issue_id=None): continue elif section is not None and section != article.section: logger.error( - "Attempted to order articles from mixed sections" - " %s" % ids + "Attempted to order articles from mixed sections" " %s" % ids ) continue models.ArticleOrdering.objects.update_or_create( issue=issue, article=article, defaults={ - 'order': order, - 'section': article.section, - } + "order": order, + "section": article.section, + }, ) - return JsonResponse({'status': 'okay'}) + return JsonResponse({"status": "okay"}) @editor_user_required @@ -1780,9 +1837,9 @@ def published_article_archive(request): :param request: request object :return: contextualised django template """ - template = 'journal/manage/published_article_archive.html' + template = "journal/manage/published_article_archive.html" context = { - 'published_articles': request.journal.archive_published_articles(), + "published_articles": request.journal.archive_published_articles(), } return render( @@ -1797,9 +1854,9 @@ def rejected_archived_article_archive(request): """ Allows an editor to view rejected and archived articles. """ - template = 'journal/manage/rejected_archived_article_archive.html' + template = "journal/manage/rejected_archived_article_archive.html" context = { - 'articles': request.journal.rejected_and_archived_articles(), + "articles": request.journal.rejected_and_archived_articles(), } return render( request, @@ -1830,19 +1887,18 @@ def manage_archive_article(request, article_id): galley_form = production_forms.GalleyForm() if request.POST: - - if 'file' in request.FILES: + if "file" in request.FILES: galley_form = production_forms.GalleyForm(request.POST, request.FILES) if galley_form.is_valid(): - for uploaded_file in request.FILES.getlist('file'): + for uploaded_file in request.FILES.getlist("file"): try: production_logic.save_galley( article, request, uploaded_file, True, - label=galley_form.cleaned_data.get('label'), - public=galley_form.cleaned_data.get('public'), + label=galley_form.cleaned_data.get("label"), + public=galley_form.cleaned_data.get("public"), ) except UnicodeDecodeError: messages.add_message( @@ -1851,22 +1907,24 @@ def manage_archive_article(request, article_id): _("Uploaded file is not UTF-8 encoded"), ) except production_logic.ZippedGalleyError: - messages.add_message(request, messages.ERROR, + messages.add_message( + request, + messages.ERROR, "Galleys must be uploaded individually, not zipped", ) else: messages.add_message( request, messages.WARNING, - 'Galley form not valid.', + "Galley form not valid.", ) - if 'delete_note' in request.POST: - note_id = int(request.POST['delete_note']) + if "delete_note" in request.POST: + note_id = int(request.POST["delete_note"]) publisher_note = submission_models.PublisherNote.objects.get(pk=note_id) publisher_note.delete() - if 'add_publisher_note' in request.POST: + if "add_publisher_note" in request.POST: pn = submission_models.PublisherNote() pn.creator = request.user pn.sequence = 0 @@ -1876,13 +1934,15 @@ def manage_archive_article(request, article_id): article.publisher_notes.add(pn) article.save() - if 'save_publisher_note' in request.POST: - note_id = int(request.POST['save_publisher_note']) + if "save_publisher_note" in request.POST: + note_id = int(request.POST["save_publisher_note"]) pn = submission_models.PublisherNote.objects.get(pk=note_id) pn_form = submission_forms.PublisherNoteForm(data=request.POST, instance=pn) pn_form.save() - return redirect(reverse('manage_archive_article', kwargs={'article_id': article.pk})) + return redirect( + reverse("manage_archive_article", kwargs={"article_id": article.pk}) + ) newnote_form = submission_forms.PublisherNoteForm() @@ -1893,20 +1953,19 @@ def manage_archive_article(request, article_id): note_forms.append(note_form) assigned_editors = [ - assignment.editor for assignment in review_models.EditorAssignment.objects.filter( - article=article - ) + assignment.editor + for assignment in review_models.EditorAssignment.objects.filter(article=article) ] - template = 'journal/manage/archive_article.html' + template = "journal/manage/archive_article.html" context = { - 'article': article, - 'galleys': galleys, - 'identifiers': identifiers, - 'newnote_form': newnote_form, - 'note_forms': note_forms, - 'galley_form': galley_form, - 'assigned_editors': assigned_editors, + "article": article, + "galleys": galleys, + "identifiers": identifiers, + "newnote_form": newnote_form, + "note_forms": note_forms, + "galley_form": galley_form, + "assigned_editors": assigned_editors, } return render(request, template, context) @@ -1919,12 +1978,13 @@ def publication_schedule(request): :param request: HttpRequest object :return: HttpReponse """ - article_list = submission_models.Article.objects.filter(journal=request.journal, - date_published__gte=timezone.now()) + article_list = submission_models.Article.objects.filter( + journal=request.journal, date_published__gte=timezone.now() + ) - template = 'journal/manage/publication_schedule.html' + template = "journal/manage/publication_schedule.html" context = { - 'articles': article_list, + "articles": article_list, } return render(request, template, context) @@ -1941,32 +2001,44 @@ def become_reviewer(request): """ # The user needs to login before we can do anything else - code = 'not-logged-in' - message = _('You must login before you can become a reviewer. Click the button below to login.') + code = "not-logged-in" + message = _( + "You must login before you can become a reviewer. Click the button below to login." + ) - if request.user and request.user.is_authenticated and not request.user.is_reviewer(request): + if ( + request.user + and request.user.is_authenticated + and not request.user.is_reviewer(request) + ): # We have a user, they are logged in and not yet a reviewer - code = 'not-reviewer' - message = _('You are not yet a reviewer for this journal. Click the button below to become a reviewer.') + code = "not-reviewer" + message = _( + "You are not yet a reviewer for this journal. Click the button below to become a reviewer." + ) - elif request.user and request.user.is_authenticated and request.user.is_reviewer(request): + elif ( + request.user + and request.user.is_authenticated + and request.user.is_reviewer(request) + ): # The user is logged in, and is already a reviewer - code = 'already-reviewer' - message = _('You are already a reviewer.') + code = "already-reviewer" + message = _("You are already a reviewer.") - if request.POST.get('action', None) == 'go': - request.user.add_account_role('reviewer', request.journal) + if request.POST.get("action", None) == "go": + request.user.add_account_role("reviewer", request.journal) messages.add_message( request, messages.SUCCESS, - _('You are now a reviewer'), + _("You are now a reviewer"), ) - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) - template = 'journal/become_reviewer.html' + template = "journal/become_reviewer.html" context = { - 'code': code, - 'message': message, + "code": code, + "message": message, } return render(request, template, context) @@ -1978,9 +2050,10 @@ def contact(request): :param request: HttpRequest object :return: HttpResponse or HttpRedirect if POST """ - subject = request.GET.get('subject', '') - contacts = core_models.Contacts.objects.filter(content_type=request.model_content_type, - object_id=request.site_type.pk) + subject = request.GET.get("subject", "") + contacts = core_models.Contacts.objects.filter( + content_type=request.model_content_type, object_id=request.site_type.pk + ) contact_form = forms.ContactForm(subject=subject, contacts=contacts) @@ -1998,19 +2071,19 @@ def contact(request): messages.add_message( request, messages.SUCCESS, - _('Your message has been sent.'), + _("Your message has been sent."), ) - return redirect(reverse('contact')) + return redirect(reverse("contact")) if request.journal and request.journal.disable_front_end: - template = 'admin/journal/contact.html' + template = "admin/journal/contact.html" elif request.journal: - template = 'journal/contact.html' + template = "journal/contact.html" else: - template = 'press/journal/contact.html' + template = "press/journal/contact.html" context = { - 'contact_form': contact_form, - 'contacts': contacts, + "contact_form": contact_form, + "contacts": contacts, } return render(request, template, context) @@ -2027,21 +2100,21 @@ def editorial_team(request, group_id=None): :return: HttpResponse object """ kwargs = { - 'journal': request.journal, - 'press': request.press, + "journal": request.journal, + "press": request.press, } if group_id: - kwargs['pk'] = group_id + kwargs["pk"] = group_id editorial_groups = core_models.EditorialGroup.objects.filter(**kwargs) if request.journal: - template = 'journal/editorial_team.html' + template = "journal/editorial_team.html" else: - template = 'press/editorial_team.html' + template = "press/editorial_team.html" context = { - 'editorial_groups': editorial_groups, - 'group_id': group_id, + "editorial_groups": editorial_groups, + "group_id": group_id, } return render(request, template, context) @@ -2055,11 +2128,11 @@ def author_list(request): :param request: HttpRequest object :return: HttpResponse object """ - author_list = request.journal.users_with_role('author') - template = 'journal/authors.html' + author_list = request.journal.users_with_role("author") + template = "journal/authors.html" context = { - 'author_list': author_list, + "author_list": author_list, } return render(request, template, context) @@ -2079,12 +2152,12 @@ def sitemap(request, issue_id=None): ) path_parts = [ request.journal.code, - '{}_sitemap.xml'.format(issue.pk), + "{}_sitemap.xml".format(issue.pk), ] else: path_parts = [ request.journal.code, - 'sitemap.xml', + "sitemap.xml", ] return core_views.sitemap( request, @@ -2099,16 +2172,17 @@ def search(request): else: return old_search(request) + @decorators.frontend_enabled def full_text_search(request): - """ Allows a user to search for articles using various filters + """Allows a user to search for articles using various filters :param request: HttpRequest object :return: HttpResponse object """ search_term = None keyword = None redir = False - sort = 'title' + sort = "title" articles = [] search_term, keyword, sort, form, redir = logic.handle_search_controls( @@ -2117,21 +2191,23 @@ def full_text_search(request): if search_term: form.is_valid() articles = submission_models.Article.objects.search( - search_term, form.get_search_filters(), + search_term, + form.get_search_filters(), sort=form.cleaned_data.get("sort"), site=request.site_object, ) - template = 'journal/full-text-search.html' + template = "journal/full-text-search.html" context = { - 'articles': articles, - 'article_search': search_term, - 'keyword': keyword, - 'form': form, + "articles": articles, + "article_search": search_term, + "keyword": keyword, + "form": form, } return render(request, template, context) + @decorators.frontend_enabled def old_search(request): """ @@ -2143,37 +2219,39 @@ def old_search(request): search_term = None keyword = None redir = False - sort = 'title' + sort = "title" search_term, keyword, sort, form, redir = logic.handle_search_controls(request) if redir: return redir from itertools import chain + if search_term: escaped = re.escape(search_term) # checks titles, keywords and subtitles first, # then matches author based on below regex split search term. split_term = [re.escape(word) for word in search_term.split(" ")] split_term.append(escaped) - search_regex = "^({})$".format( - "|".join({name for name in split_term}) - ) - articles = submission_models.Article.objects.filter( - ( - Q(title__icontains=search_term) | - Q(keywords__word__iregex=search_regex) | - Q(subtitle__icontains=search_term) + search_regex = "^({})$".format("|".join({name for name in split_term})) + articles = ( + submission_models.Article.objects.filter( + ( + Q(title__icontains=search_term) + | Q(keywords__word__iregex=search_regex) + | Q(subtitle__icontains=search_term) + ) + | ( + Q(frozenauthor__first_name__iregex=search_regex) + | Q(frozenauthor__last_name__iregex=search_regex) + ), + journal=request.journal, + stage=submission_models.STAGE_PUBLISHED, + date_published__lte=timezone.now(), ) - | - ( - Q(frozenauthor__first_name__iregex=search_regex) | - Q(frozenauthor__last_name__iregex=search_regex) - ), - journal=request.journal, - stage=submission_models.STAGE_PUBLISHED, - date_published__lte=timezone.now() - ).distinct().order_by(sort) + .distinct() + .order_by(sort) + ) # just single keyword atm. but keyword is included in article_search. elif keyword: @@ -2181,24 +2259,28 @@ def old_search(request): keywords__word=keyword, journal=request.journal, stage=submission_models.STAGE_PUBLISHED, - date_published__lte=timezone.now() + date_published__lte=timezone.now(), ).order_by(sort) keyword_limit = 20 - popular_keywords = submission_models.Keyword.objects.filter( - article__journal=request.journal, - article__stage=submission_models.STAGE_PUBLISHED, - article__date_published__lte=timezone.now(), - ).annotate(articles_count=Count('article')).order_by("-articles_count")[:keyword_limit] + popular_keywords = ( + submission_models.Keyword.objects.filter( + article__journal=request.journal, + article__stage=submission_models.STAGE_PUBLISHED, + article__date_published__lte=timezone.now(), + ) + .annotate(articles_count=Count("article")) + .order_by("-articles_count")[:keyword_limit] + ) - template = 'journal/search.html' + template = "journal/search.html" context = { - 'articles': articles, - 'article_search': search_term, - 'keyword': keyword, - 'form': form, - 'sort': sort, - 'all_keywords': popular_keywords + "articles": articles, + "article_search": search_term, + "keyword": keyword, + "form": form, + "sort": sort, + "all_keywords": popular_keywords, } return render(request, template, context) @@ -2211,21 +2293,23 @@ def submissions(request): :param request: HttpRequest object :return: HttpResponse object """ - template = 'journal/submissions.html' + template = "journal/submissions.html" if request.journal.disable_front_end: - template = 'admin/journal/submissions.html' + template = "admin/journal/submissions.html" context = { - 'sections': submission_models.Section.objects.filter( + "sections": submission_models.Section.objects.filter( journal=request.journal, public_submissions=True, ), - 'licenses': submission_models.Licence.objects.filter( + "licenses": submission_models.Licence.objects.filter( journal=request.journal, available_for_submission=True, ), - 'submission_items': cms_models.SubmissionItem.objects.filter(journal=request.journal) + "submission_items": cms_models.SubmissionItem.objects.filter( + journal=request.journal + ), } return render(request, template, context) @@ -2245,17 +2329,21 @@ def manage_article_log(request, article_id): journal=request.journal, ) content_type = ContentType.objects.get_for_model(article) - log_entries = utils_models.LogEntry.objects.filter(content_type=content_type, object_id=article.pk) + log_entries = utils_models.LogEntry.objects.filter( + content_type=content_type, object_id=article.pk + ) if request.POST and settings.ENABLE_ENHANCED_MAILGUN_FEATURES: - call_command('check_mailgun_stat', article_id=article_id) - return redirect(reverse('manage_article_log', kwargs={'article_id': article.pk})) + call_command("check_mailgun_stat", article_id=article_id) + return redirect( + reverse("manage_article_log", kwargs={"article_id": article.pk}) + ) - template = 'journal/article_log.html' + template = "journal/article_log.html" context = { - 'article': article, - 'log_entries': log_entries, - 'return': request.GET.get('return', None) + "article": article, + "log_entries": log_entries, + "return": request.GET.get("return", None), } return render(request, template, context) @@ -2272,19 +2360,19 @@ def resend_logged_email(request, article_id, log_id): form = forms.ResendEmailForm(log_entry=log_entry) close = False - if request.POST and 'resend' in request.POST: + if request.POST and "resend" in request.POST: form = forms.ResendEmailForm(request.POST, log_entry=log_entry) if form.is_valid(): logic.resend_email(article, log_entry, request, form) close = True - template = 'journal/resend_logged_email.html' + template = "journal/resend_logged_email.html" context = { - 'article': article, - 'log_entry': log_entry, - 'form': form, - 'close': close, + "article": article, + "log_entry": log_entry, + "form": form, + "close": close, } return render(request, template, context) @@ -2295,8 +2383,7 @@ def resend_logged_email(request, article_id, log_id): def send_user_email(request, user_id, article_id=None): user = get_object_or_404(core_models.Account, pk=user_id) form = core_forms.EmailForm( - initial={'body': '
{signature}'.format( - signature=request.user.signature)}, + initial={"body": "
{signature}".format(signature=request.user.signature)}, ) close = False article = None @@ -2308,17 +2395,17 @@ def send_user_email(request, user_id, article_id=None): journal=request.journal, ) - if request.POST and 'send' in request.POST: + if request.POST and "send" in request.POST: form = core_forms.EmailForm( request.POST, request.FILES, ) if form.is_valid(): log_dict = { - 'level': 'Info', - 'action_text': f'{request.user} sent an email to {user.full_name}', - 'types': 'Email', - 'target': article if article else user, + "level": "Info", + "action_text": f"{request.user} sent an email to {user.full_name}", + "types": "Email", + "target": article if article else user, } core_email.send_email( user, @@ -2329,12 +2416,12 @@ def send_user_email(request, user_id, article_id=None): ) close = True - template = 'admin/journal/send_user_email.html' + template = "admin/journal/send_user_email.html" context = { - 'user': user, - 'close': close, - 'form': form, - 'article': article, + "user": user, + "close": close, + "form": form, + "article": article, } return render(request, template, context) @@ -2355,8 +2442,7 @@ def new_note(request, article_id): ) if request.POST: - - note = request.POST.get('note') + note = request.POST.get("note") sav_note = submission_models.Note.objects.create( article=article, @@ -2364,13 +2450,16 @@ def new_note(request, article_id): text=note, ) - return_dict = {'id': sav_note.pk, 'note': sav_note.text, 'initials': sav_note.creator.initials(), - 'date_time': str(sav_note.date_time), - 'html': logic.create_html_snippet(sav_note)} + return_dict = { + "id": sav_note.pk, + "note": sav_note.text, + "initials": sav_note.creator.initials(), + "date_time": str(sav_note.date_time), + "html": logic.create_html_snippet(sav_note), + } else: - - return_dict = {'error': 'This request must be made with POST'} + return_dict = {"error": "This request must be made with POST"} return HttpResponse(json.dumps(return_dict), content_type="application/json") @@ -2393,8 +2482,11 @@ def delete_note(request, article_id, note_id): def download_journal_file(request, file_id): file = get_object_or_404(core_models.File, pk=file_id) - if file.privacy == 'public' or (request.user.is_authenticated and request.user.is_staff) or \ - (request.user.is_authenticated and request.user.is_editor(request)): + if ( + file.privacy == "public" + or (request.user.is_authenticated and request.user.is_staff) + or (request.user.is_authenticated and request.user.is_editor(request)) + ): return files.serve_journal_cover(request, file) else: raise Http404 @@ -2409,14 +2501,16 @@ def download_table(request, identifier_type, identifier, table_name): :param table_name: The ID of the table inside the HTML :return: StreamingHTTPResponse with CSV attached """ - article = submission_models.Article.get_article(request.journal, identifier_type, identifier) + article = submission_models.Article.get_article( + request.journal, identifier_type, identifier + ) galley = article.get_render_galley - if galley.file.mime_type.endswith('/xml'): + if galley.file.mime_type.endswith("/xml"): content = galley.file_content() table = logic.get_table_from_html(table_name, content) csv = logic.parse_html_table_to_csv(table, table_name) - return files.serve_temp_file(csv, '{0}.csv'.format(table_name)) + return files.serve_temp_file(csv, "{0}.csv".format(table_name)) def download_supp_file(request, article_id, supp_file_id): @@ -2438,10 +2532,10 @@ def download_supp_file(request, article_id, supp_file_id): def texture_edit(request, file_id): file = get_object_or_404(core_models.File, pk=file_id) - template = 'admin/journal/texture.html' + template = "admin/journal/texture.html" context = { - 'file': file, - 'content': files.get_file(file, file.article).replace('\n', '') + "file": file, + "content": files.get_file(file, file.article).replace("\n", ""), } return render(request, template, context) @@ -2455,15 +2549,17 @@ def document_management(request, article_id): journal=request.journal, ) article_files = core_models.File.objects.filter(article_id=document_article.pk) - return_url = request.GET.get('return', '/dashboard/') + return_url = request.GET.get("return", "/dashboard/") if request.POST and request.FILES: + label = ( + request.POST.get("label") if request.POST.get("label") else "[Unlabeled]" + ) - label = request.POST.get('label') if request.POST.get('label') else '[Unlabeled]' - - if 'manu' in request.POST: + if "manu" in request.POST: from core import files as core_files - file = request.FILES.get('manu-file') + + file = request.FILES.get("manu-file") new_file = core_files.save_file_to_article( file, document_article, @@ -2475,12 +2571,13 @@ def document_management(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Manuscript file uploaded.'), + _("Manuscript file uploaded."), ) - if 'fig' in request.POST: + if "fig" in request.POST: from core import files as core_files - file = request.FILES.get('fig-file') + + file = request.FILES.get("fig-file") new_file = core_files.save_file_to_article( file, document_article, @@ -2492,33 +2589,36 @@ def document_management(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Data/figure file uploaded.'), + _("Data/figure file uploaded."), ) - if 'prod' in request.POST: + if "prod" in request.POST: # Deprecated. Use manuscript file instead. from production import logic as prod_logic - file = request.FILES.get('prod-file') + + file = request.FILES.get("prod-file") prod_logic.save_prod_file(document_article, request, file, label) messages.add_message( request, messages.SUCCESS, - _('Production file uploaded.'), + _("Production file uploaded."), ) - if 'galley' in request.POST: + if "galley" in request.POST: from production import logic as prod_logic - file = request.FILES.get('galley-file') + + file = request.FILES.get("galley-file") prod_logic.save_galley(document_article, request, file, True, label) messages.add_message( request, messages.SUCCESS, - _('Galley file uploaded.'), + _("Galley file uploaded."), ) - if 'proofing' in request.POST: + if "proofing" in request.POST: from core import files as core_files - file = request.FILES.get('proofing-file') + + file = request.FILES.get("proofing-file") new_file = core_files.save_file_to_article( file, document_article, @@ -2526,7 +2626,7 @@ def document_management(request, article_id): label=label, ) try: - typesetting_models = import_module('plugins.typesetting.models') + typesetting_models = import_module("plugins.typesetting.models") rounds = typesetting_models.TypesettingRound.objects.filter( article=document_article, ) @@ -2536,7 +2636,7 @@ def document_management(request, article_id): current_round = typesetting_models.TypesettingRound.objects.create( article=document_article, ) - request.user.add_account_role('proofreader', request.journal) + request.user.add_account_role("proofreader", request.journal) proofing = typesetting_models.GalleyProofing.objects.create( round=current_round, manager=request.user, @@ -2549,50 +2649,51 @@ def document_management(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Proofing file uploaded.'), + _("Proofing file uploaded."), ) except ModuleNotFoundError: messages.add_message( request, messages.SUCCESS, - _('Saved file.'), + _("Saved file."), ) - if 'supp' in request.POST: + if "supp" in request.POST: from production import logic as prod_logic - file = request.FILES.get('supp-file') + + file = request.FILES.get("supp-file") prod_logic.save_supp_file(document_article, request, file, label) messages.add_message( request, messages.SUCCESS, - _('Supplementary file uploaded.'), + _("Supplementary file uploaded."), ) - if 'source' in request.POST: + if "source" in request.POST: from production import logic as prod_logic - file = request.FILES.get('source-file') + + file = request.FILES.get("source-file") prod_logic.save_source_file(document_article, request, file, label) messages.add_message( request, messages.SUCCESS, - _('Typesetting source file uploaded.'), + _("Typesetting source file uploaded."), ) return redirect( - '{0}?return={1}'.format( + "{0}?return={1}".format( reverse( - 'document_management', - kwargs={'article_id': document_article.pk} + "document_management", kwargs={"article_id": document_article.pk} ), - return_url + return_url, ) ) - template = 'admin/journal/document_management.html' + template = "admin/journal/document_management.html" context = { - 'files': article_files, - 'article': document_article, - 'return_url': return_url, + "files": article_files, + "article": document_article, + "return_url": return_url, } return render(request, template, context) @@ -2613,7 +2714,7 @@ def download_issue(request, issue_id): store_article_access( request, article, - 'download', + "download", galley_type=galley.type, ) galley_files.append(galley.file) @@ -2671,10 +2772,11 @@ def serve_article_xml(request, identifier_type, identifier): ) if xml_galleys.exists(): - if xml_galleys.count() > 1: - logger.error("Found multiple XML galleys for article {id}, " - "returning first match".format(id=article_object.pk)) + logger.error( + "Found multiple XML galleys for article {id}, " + "returning first match".format(id=article_object.pk) + ) xml_galley = xml_galleys[0] else: @@ -2744,61 +2846,64 @@ def serve_article_pdf(request, identifier_type, identifier): @editor_user_required def manage_languages(request): active_languages = request.journal.get_setting( - 'general', 'journal_languages', + "general", + "journal_languages", ) if request.POST: - if 'default' in request.POST: - new_default = request.POST.get('default') + if "default" in request.POST: + new_default = request.POST.get("default") if new_default in active_languages: setting_handler.save_setting( - setting_group_name='general', - setting_name='default_journal_language', + setting_group_name="general", + setting_name="default_journal_language", journal=request.journal, value=new_default, ) messages.add_message( request, messages.SUCCESS, - 'Default language now set to {}'.format(new_default), + "Default language now set to {}".format(new_default), ) else: messages.add_message( request, messages.WARNING, - '{} is not an active language for this journal.'.format(new_default), + "{} is not an active language for this journal.".format( + new_default + ), ) - if 'enable' in request.POST: - lang_to_enable = request.POST.get('enable') + if "enable" in request.POST: + lang_to_enable = request.POST.get("enable") active_languages.append(lang_to_enable) messages.add_message( request, messages.SUCCESS, - '{} enabled.'.format(lang_to_enable), + "{} enabled.".format(lang_to_enable), ) - if 'disable' in request.POST: - lang_to_delete = request.POST.get('disable') + if "disable" in request.POST: + lang_to_delete = request.POST.get("disable") active_languages.remove(lang_to_delete) messages.add_message( request, messages.ERROR, - '{} disabled.'.format(lang_to_delete), + "{} disabled.".format(lang_to_delete), ) active_languages.append(settings.LANGUAGE_CODE) setting_handler.save_setting( - setting_group_name='general', - setting_name='journal_languages', + setting_group_name="general", + setting_name="journal_languages", journal=request.journal, value=active_languages, ) return redirect( reverse( - 'manage_languages', + "manage_languages", ) ) - template = 'admin/journal/manage/languages.html' + template = "admin/journal/manage/languages.html" context = { - 'active_languages': active_languages, + "active_languages": active_languages, } return render( request, @@ -2815,25 +2920,24 @@ class FacetedArticlesListView(core_views.GenericFacetedListView): Do not use this view directly. This view can also be subclassed and modified for use with other models. """ + model = submission_models.Article - template_name = 'core/manager/article_list.html' + template_name = "core/manager/article_list.html" def get_queryset(self, params_querydict=None): self.queryset = super().get_queryset(params_querydict=params_querydict) - return self.queryset.exclude( - stage=submission_models.STAGE_UNSUBMITTED - ) + return self.queryset.exclude(stage=submission_models.STAGE_UNSUBMITTED) -@method_decorator(has_journal, name='dispatch') -@method_decorator(decorators.frontend_enabled, name='dispatch') +@method_decorator(has_journal, name="dispatch") +@method_decorator(decorators.frontend_enabled, name="dispatch") class PublishedArticlesListView(FacetedArticlesListView): - """ A list of published articles that can be searched, sorted, and filtered """ - template_name = 'journal/article_list.html' + + template_name = "journal/article_list.html" def get_queryset(self, params_querydict=None): self.queryset = super().get_queryset(params_querydict) @@ -2844,51 +2948,51 @@ def get_queryset(self, params_querydict=None): def get_facets(self): facets = { - 'date_published__date__gte': { - 'type': 'date', - 'field_label': _('Published after'), + "date_published__date__gte": { + "type": "date", + "field_label": _("Published after"), }, - 'date_published__date__lte': { - 'type': 'date', - 'field_label': _('Published before'), + "date_published__date__lte": { + "type": "date", + "field_label": _("Published before"), }, - 'section__pk': { - 'type': 'foreign_key', - 'model': submission_models.Section, - 'field_label': _('Section'), - 'choice_label_field': 'name', + "section__pk": { + "type": "foreign_key", + "model": submission_models.Section, + "field_label": _("Section"), + "choice_label_field": "name", }, } return self.filter_facets_if_journal(facets) def get_order_by_choices(self): return [ - ('-date_published', _('Newest')), - ('date_published', _('Oldest')), - ('title', _('Titles A-Z')), - ('-title', _('Titles Z-A')), - ('correspondence_author__last_name', _('Author Name')), - ('primary_issue__volume', _('Volume')), + ("-date_published", _("Newest")), + ("date_published", _("Oldest")), + ("title", _("Titles A-Z")), + ("-title", _("Titles Z-A")), + ("correspondence_author__last_name", _("Author Name")), + ("primary_issue__volume", _("Volume")), ] def get_order_by(self): - order_by = self.request.GET.get('order_by', '-date_published') + order_by = self.request.GET.get("order_by", "-date_published") order_by_choices = self.get_order_by_choices() - return order_by if order_by in dict(order_by_choices) else '' + return order_by if order_by in dict(order_by_choices) else "" def order_queryset(self, queryset): order_by = self.get_order_by() if order_by: - return queryset.order_by('pinnedarticle__sequence', order_by) + return queryset.order_by("pinnedarticle__sequence", order_by) else: - return queryset.order_by('pinnedarticle__sequence') + return queryset.order_by("pinnedarticle__sequence") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['search_form'] = forms.SearchForm() + context["search_form"] = forms.SearchForm() return context -@method_decorator(editor_user_required, name='dispatch') +@method_decorator(editor_user_required, name="dispatch") class JournalUsers(core_views.BaseUserList): pass diff --git a/src/manage.py b/src/manage.py index 38c08e10bd..641b00017b 100755 --- a/src/manage.py +++ b/src/manage.py @@ -11,6 +11,7 @@ if __name__ == "__main__": from django.core.management import execute_from_command_line + load_janeway_settings() execute_from_command_line(sys.argv) diff --git a/src/metrics/admin.py b/src/metrics/admin.py index ea300e738b..f67bb36221 100755 --- a/src/metrics/admin.py +++ b/src/metrics/admin.py @@ -16,8 +16,8 @@ def _export_as_csv(self, request, queryset): meta = self.model._meta field_names = [field.name for field in meta.fields] - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta) + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = "attachment; filename={}.csv".format(meta) writer = csv.writer(response) writer.writerow(field_names) @@ -29,43 +29,70 @@ def _export_as_csv(self, request, queryset): class ArticleAccessAdmin(admin_utils.ArticleFKModelAdmin): """Displays objects in the Django admin interface.""" - list_display = ('_article', 'accessed', 'country', - 'type', 'galley_type', '_journal') - list_filter = ('article__journal', 'accessed', 'type', 'galley_type',) - search_fields = ('article__title', 'article__pk', 'identifier', - 'article__journal__code', - 'type', 'galley_type', 'accessed', - 'country__name') - raw_id_fields = ('article',) - date_hierarchy = ('accessed') + + list_display = ( + "_article", + "accessed", + "country", + "type", + "galley_type", + "_journal", + ) + list_filter = ( + "article__journal", + "accessed", + "type", + "galley_type", + ) + search_fields = ( + "article__title", + "article__pk", + "identifier", + "article__journal__code", + "type", + "galley_type", + "accessed", + "country__name", + ) + raw_id_fields = ("article",) + date_hierarchy = "accessed" def get_form(self, request, obj=None, **kwargs): form = super(ArticleAccessAdmin, self).get_form(request, obj, **kwargs) - form.base_fields['article'].queryset = submission_models.Article.objects.all() + form.base_fields["article"].queryset = submission_models.Article.objects.all() return form class HistoricArticleAccessAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_article', 'views', 'downloads', '_journal') - list_filter = ('article__journal',) - raw_id_fields = ('article',) + list_display = ("_article", "views", "downloads", "_journal") + list_filter = ("article__journal",) + raw_id_fields = ("article",) class AltMetricAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_article', 'source', 'pid', '_journal') - list_filter = ('article__journal', 'source', 'timestamp') - search_fields = ('article__title', 'article__pk', 'source', - 'pid') - date_hierarchy = ('timestamp') - raw_id_fields = ('article',) + list_display = ("_article", "source", "pid", "_journal") + list_filter = ("article__journal", "source", "timestamp") + search_fields = ("article__title", "article__pk", "source", "pid") + date_hierarchy = "timestamp" + raw_id_fields = ("article",) class ArticleLinkAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', 'object_type', 'doi', 'year', '_article', - '_journal') - list_filter = ('article__journal', 'object_type', 'year',) - search_fields = ('article__title', 'article__pk', 'doi', 'year', - 'article_title', 'journal_title', 'journal_issn') + list_display = ("pk", "object_type", "doi", "year", "_article", "_journal") + list_filter = ( + "article__journal", + "object_type", + "year", + ) + search_fields = ( + "article__title", + "article__pk", + "doi", + "year", + "article_title", + "journal_title", + "journal_issn", + ) actions = ["export_as_csv"] @@ -76,11 +103,21 @@ def export_as_csv(self, request, queryset): class BookLinkAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', 'object_type', 'doi', 'year', '_article', - '_journal') - list_filter = ('article__journal', 'object_type', 'year',) - search_fields = ('article__title', 'article__pk', 'doi', 'year', - 'title', 'isbn_print', 'isbn_electronic') + list_display = ("pk", "object_type", "doi", "year", "_article", "_journal") + list_filter = ( + "article__journal", + "object_type", + "year", + ) + search_fields = ( + "article__title", + "article__pk", + "doi", + "year", + "title", + "isbn_print", + "isbn_electronic", + ) actions = ["export_as_csv"] diff --git a/src/metrics/logic.py b/src/metrics/logic.py index 7a35456687..bafdbb2f1d 100755 --- a/src/metrics/logic.py +++ b/src/metrics/logic.py @@ -38,43 +38,42 @@ def get_press_totals(start_date, end_date, report_months, compat=False, do_yop=F year_of_publication = {} for date in report_months: - press_months['{0}-{1}'.format(date.strftime('%b'), date.year)] = '' + press_months["{0}-{1}".format(date.strftime("%b"), date.year)] = "" for journal_object in journal_models.Journal.objects.all(): - journal = {} - journal['journal'] = journal_object + journal["journal"] = journal_object - journal['total'] = 0 - journal['total_views'] = 0 - journal['total_downloads'] = 0 + journal["total"] = 0 + journal["total_views"] = 0 + journal["total_downloads"] = 0 - journal['reporting_periods'] = [] + journal["reporting_periods"] = [] # year of publication is for COUNTER journal report 5 # it needs to have "each YOP in the current decade and in the immediately previous decade as separate columns" # easiest way to do this is simply to count backwards 19 years as the theoretical maximum - journal['year_of_publication'] = {} + journal["year_of_publication"] = {} year = timezone.now().year for year_counter in range(year, year - 19, -1): - year_of_publication[year] = '' + year_of_publication[year] = "" for date in report_months: - month = '{0}-{1}'.format(date.strftime('%b'), date.year) + month = "{0}-{1}".format(date.strftime("%b"), date.year) # setting these to zero for now for compat with pycounter # the spec says they should be set to "" so we may need to change that back # logic to handle this is included below if compat: journal[month] = 0 - journal['{0}-views'.format(month)] = 0 - journal['{0}-downloads'.format(month)] = 0 + journal["{0}-views".format(month)] = 0 + journal["{0}-downloads".format(month)] = 0 else: - journal[month] = '' - journal['{0}-views'.format(month)] = '' - journal['{0}-downloads'.format(month)] = '' + journal[month] = "" + journal["{0}-views".format(month)] = "" + journal["{0}-downloads".format(month)] = "" articles = submission_models.Article.objects.filter(journal=journal_object) @@ -85,33 +84,40 @@ def get_press_totals(start_date, end_date, report_months, compat=False, do_yop=F downloads = get_article_downloads(article) if article.date_published.year in year_of_publication: - if year_of_publication[article.date_published.year] == '': + if year_of_publication[article.date_published.year] == "": year_of_publication[article.date_published.year] = 0 - if journal['year_of_publication'][article.date_published.year] == '': - journal['year_of_publication'][article.date_published.year] = 0 + if ( + journal["year_of_publication"][article.date_published.year] + == "" + ): + journal["year_of_publication"][article.date_published.year] = 0 year_of_publication[article.date_published.year] += views + downloads - journal['year_of_publication'][article.date_published.year] += views + downloads + journal["year_of_publication"][article.date_published.year] += ( + views + downloads + ) for article in articles: - views = models.ArticleAccess.objects.filter(type='view', article=article, - accessed__range=[start_date, end_date]) + views = models.ArticleAccess.objects.filter( + type="view", article=article, accessed__range=[start_date, end_date] + ) - downloads = models.ArticleAccess.objects.filter(type='download', article=article, - accessed__range=[start_date, end_date]) + downloads = models.ArticleAccess.objects.filter( + type="download", article=article, accessed__range=[start_date, end_date] + ) view_count = views.count() download_count = downloads.count() # total views and downloads - journal['total'] += view_count + download_count + journal["total"] += view_count + download_count # total views - journal['total_views'] += view_count + journal["total_views"] += view_count # total downloads - journal['total_downloads'] += download_count + journal["total_downloads"] += download_count # add the totals to the press totals view_access_count += view_count @@ -119,38 +125,43 @@ def get_press_totals(start_date, end_date, report_months, compat=False, do_yop=F for view_object in views: # get the date for this access - access_date = '{0}-{1}'.format(view_object.accessed.strftime('%b'), view_object.accessed.year) + access_date = "{0}-{1}".format( + view_object.accessed.strftime("%b"), view_object.accessed.year + ) # we have to handle this like this since data for months already collected must be blank, not zero - if journal[access_date] == '': + if journal[access_date] == "": journal[access_date] = 0 - if press_months[access_date] == '': + if press_months[access_date] == "": press_months[access_date] = 0 - if journal['{0}-views'.format(access_date)] == '': - journal['{0}-views'.format(access_date)] = 0 + if journal["{0}-views".format(access_date)] == "": + journal["{0}-views".format(access_date)] = 0 journal[access_date] += 1 - journal['{0}-views'.format(access_date)] += 1 + journal["{0}-views".format(access_date)] += 1 press_months[access_date] += 1 for download_object in downloads: # get the date for this access - access_date = '{0}-{1}'.format(download_object.accessed.strftime('%b'), download_object.accessed.year) + access_date = "{0}-{1}".format( + download_object.accessed.strftime("%b"), + download_object.accessed.year, + ) # we have to handle this like this since data for months already collected must be blank, not zero - if journal[access_date] == '': + if journal[access_date] == "": journal[access_date] = 0 - if press_months[access_date] == '': + if press_months[access_date] == "": press_months[access_date] = 0 - if journal['{0}-downloads'.format(access_date)] == '': - journal['{0}-downloads'.format(access_date)] = 0 + if journal["{0}-downloads".format(access_date)] == "": + journal["{0}-downloads".format(access_date)] = 0 journal[access_date] += 1 - journal['{0}-downloads'.format(access_date)] += 1 + journal["{0}-downloads".format(access_date)] += 1 press_months[access_date] += 1 for date in report_months: @@ -158,29 +169,49 @@ def get_press_totals(start_date, end_date, report_months, compat=False, do_yop=F # a start date # an end date # a total number of views - month = '{0}-{1}'.format(date.strftime('%b'), date.year) - journal['reporting_periods'].append(('{0}-01'.format(date.strftime('%Y-%m')), - '{0}-{1}'.format(date.strftime('%Y-%m'), - calendar.monthrange(date.year, date.month)[1]), - journal[month], - journal['{0}-views'.format(month)], - journal['{0}-downloads'.format(month)])) + month = "{0}-{1}".format(date.strftime("%b"), date.year) + journal["reporting_periods"].append( + ( + "{0}-01".format(date.strftime("%Y-%m")), + "{0}-{1}".format( + date.strftime("%Y-%m"), + calendar.monthrange(date.year, date.month)[1], + ), + journal[month], + journal["{0}-views".format(month)], + journal["{0}-downloads".format(month)], + ) + ) journals.append(journal) - return view_access_count + download_access_count, view_access_count, download_access_count, press_months, journals + return ( + view_access_count + download_access_count, + view_access_count, + download_access_count, + press_months, + journals, + ) def get_article_views(article): - historic_record, created = models.HistoricArticleAccess.objects.get_or_create(article=article) - view_access_count = models.ArticleAccess.objects.filter(type='view', article=article).count() + historic_record, created = models.HistoricArticleAccess.objects.get_or_create( + article=article + ) + view_access_count = models.ArticleAccess.objects.filter( + type="view", article=article + ).count() return historic_record.views + view_access_count def get_article_downloads(article): - historic_record, created = models.HistoricArticleAccess.objects.get_or_create(article=article) - download_access_count = models.ArticleAccess.objects.filter(type='download', article=article).count() + historic_record, created = models.HistoricArticleAccess.objects.get_or_create( + article=article + ) + download_access_count = models.ArticleAccess.objects.filter( + type="download", article=article + ).count() return historic_record.downloads + download_access_count @@ -197,7 +228,7 @@ def get_altmetrics(article): alm_dict[metric.source] = 1 total += 1 - alm_dict['total'] = total + alm_dict["total"] = total return alm_dict @@ -218,17 +249,16 @@ def store_article_access(request, article, access_type, galley_type=None): current_time = timezone.now() try: - user_agent = parse_ua_string(request.META.get('HTTP_USER_AGENT', None)) + user_agent = parse_ua_string(request.META.get("HTTP_USER_AGENT", None)) except TypeError: user_agent = None ip = shared.get_ip_address(request) iso_country_code = get_iso_country_code(ip) country = iso_to_country_object(iso_country_code) - counter_tracking_id = request.session.get('counter_tracking') + counter_tracking_id = request.session.get("counter_tracking") if user_agent and not user_agent.is_bot: - if counter_tracking_id: identifier = counter_tracking_id else: @@ -236,7 +266,7 @@ def store_article_access(request, article, access_type, galley_type=None): ip=ip, agent=user_agent, secret_key=settings.SECRET_KEY, - ).encode('utf-8') + ).encode("utf-8") identifier = hashlib.sha512(string).hexdigest() # check if the current IP has accessed this article recently. @@ -261,9 +291,9 @@ def store_article_access(request, article, access_type, galley_type=None): ) # Raise the Article Access event. event_kwargs = { - 'article_access': access, - 'article': article, - 'request': request, + "article_access": access, + "article": article, + "request": request, } event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_ACCESS, @@ -288,7 +318,7 @@ def get_view_and_download_totals(articles): def iso_to_country_object(code): if settings.DEBUG: - code = 'GB' + code = "GB" try: return core_models.Country.objects.get(code=code) except core_models.Country.DoesNotExist: @@ -298,16 +328,16 @@ def iso_to_country_object(code): def get_iso_country_code(ip): db_path = os.path.join( settings.BASE_DIR, - 'metrics', - 'geolocation', - 'GeoLite2-Country.mmdb', + "metrics", + "geolocation", + "GeoLite2-Country.mmdb", ) reader = geoip2.database.Reader(db_path) try: response = reader.country(ip) - return response.country.iso_code if response.country.iso_code else 'OTHER' + return response.country.iso_code if response.country.iso_code else "OTHER" except AddressNotFoundError: - if ip == '127.0.0.1': + if ip == "127.0.0.1": return "GB" - return 'OTHER' + return "OTHER" diff --git a/src/metrics/management/commands/accesses_to_historic.py b/src/metrics/management/commands/accesses_to_historic.py index cf8186c2c2..f858d76779 100755 --- a/src/metrics/management/commands/accesses_to_historic.py +++ b/src/metrics/management/commands/accesses_to_historic.py @@ -17,12 +17,12 @@ class Command(BaseCommand): help = "Tidies ArticleAccess objects into HistoricArticleAccess params." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--dump_data', action='store_true', default=False) + parser.add_argument("--dump_data", action="store_true", default=False) def handle(self, *args, **options): """Tidies up access records into historic accesses after 24 months has passed (COUNTER). @@ -33,22 +33,28 @@ def handle(self, *args, **options): """ date_to_tidy = timezone.now() - timedelta(weeks=104) - article_accesses = models.ArticleAccess.objects.filter(accessed__lte=date_to_tidy) - - if options.get('dump_data') and article_accesses: - path = os.path.join(settings.BASE_DIR, 'files', 'data_backup', date_to_tidy.strftime('%Y-%m-%d %H:%M')) + article_accesses = models.ArticleAccess.objects.filter( + accessed__lte=date_to_tidy + ) + + if options.get("dump_data") and article_accesses: + path = os.path.join( + settings.BASE_DIR, + "files", + "data_backup", + date_to_tidy.strftime("%Y-%m-%d %H:%M"), + ) if not os.path.exists(path): os.makedirs(path) - write_path = os.path.join(path, 'article_accesses.json') - with open(write_path, 'w+', encoding="utf-8") as f: + write_path = os.path.join(path, "article_accesses.json") + with open(write_path, "w+", encoding="utf-8") as f: data = serializers.serialize("json", article_accesses, indent=4) f.write(data) for access in article_accesses: - - if access.type == 'view': + if access.type == "view": access.article.historicarticleaccess.add_one_view() - elif access.type == 'download': + elif access.type == "download": access.article.historicarticleaccess.add_one_download() access.delete() diff --git a/src/metrics/management/commands/fetch_forward_links.py b/src/metrics/management/commands/fetch_forward_links.py index a01739a06b..4e2de31fbe 100644 --- a/src/metrics/management/commands/fetch_forward_links.py +++ b/src/metrics/management/commands/fetch_forward_links.py @@ -11,53 +11,55 @@ def process_article(link, article): - print('Processing article', end='... ') + print("Processing article", end="... ") doi = link.doi.contents[0] - volume = link.volume.contents[0] if link.volume else '' - issue = link.issue.contents[0] if link.issue else '' + volume = link.volume.contents[0] if link.volume else "" + issue = link.issue.contents[0] if link.issue else "" issn = link.issn.contents[0] if link.issn else None defaults = { - 'year': link.year.contents[0], - 'journal_title': link.journal_title.contents[0], - 'article_title': link.article_title.contents[0], - 'volume': volume, - 'issue': issue, - 'journal_issn': issn, + "year": link.year.contents[0], + "journal_title": link.journal_title.contents[0], + "article_title": link.article_title.contents[0], + "volume": volume, + "issue": issue, + "journal_issn": issn, } models.ArticleLink.objects.get_or_create( article=article, doi=doi, - object_type='article', + object_type="article", defaults=defaults, ) - print('[ok]') + print("[ok]") def process_book(link, article): - print('Processing book', end='... ') + print("Processing book", end="... ") doi = link.doi.contents[0] - isbn_print = link.find('isbn', {'type': 'print'}) - isbn_elec = link.find('isbn', {'type': 'electronic'}) + isbn_print = link.find("isbn", {"type": "print"}) + isbn_elec = link.find("isbn", {"type": "electronic"}) title = link.volume_title.contents[0] defaults = { - 'year': link.year.contents[0], - 'title': title, - 'component_number': link.component_number.contents[0] if link.component_number else '', - 'isbn_print': isbn_print.contents[0] if isbn_print else '', - 'isbn_electronic': isbn_elec.contents[0] if isbn_elec else '', + "year": link.year.contents[0], + "title": title, + "component_number": link.component_number.contents[0] + if link.component_number + else "", + "isbn_print": isbn_print.contents[0] if isbn_print else "", + "isbn_electronic": isbn_elec.contents[0] if isbn_elec else "", } models.BookLink.objects.get_or_create( article=article, doi=doi, - object_type='book', + object_type="book", defaults=defaults, ) - print('[ok]') + print("[ok]") class Command(BaseCommand): @@ -68,13 +70,13 @@ class Command(BaseCommand): help = "Pulls forward links and creates altmetrics." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?') - parser.add_argument('date', default='2000-01-01', nargs='?') + parser.add_argument("journal_code", nargs="?") + parser.add_argument("date", default="2000-01-01", nargs="?") def handle(self, *args, **options): """Pulls forward links @@ -83,8 +85,8 @@ def handle(self, *args, **options): :param options: None :return: None """ - date = options.get('date', '2000-01-01') - journal_code = options.get('journal_code', None) + date = options.get("date", "2000-01-01") + journal_code = options.get("journal_code", None) if journal_code: journals = jm.Journal.objects.filter(code=journal_code) @@ -92,75 +94,73 @@ def handle(self, *args, **options): journals = jm.Journal.objects.all() for journal in journals: - use_crossref = setting_handler.get_setting( - 'Identifiers', - 'use_crossref', + "Identifiers", + "use_crossref", journal, ).processed_value if use_crossref: - usr = setting_handler.get_setting( - 'Identifiers', - 'crossref_username', + "Identifiers", + "crossref_username", journal, ).value pwd = setting_handler.get_setting( - 'Identifiers', - 'crossref_password', + "Identifiers", + "crossref_password", journal, ).value doi = setting_handler.get_setting( - 'Identifiers', - 'crossref_prefix', + "Identifiers", + "crossref_prefix", journal, ).value payload = { - 'usr': usr, - 'pwd': pwd, - 'doi': doi, - 'startDate': date, + "usr": usr, + "pwd": pwd, + "doi": doi, + "startDate": date, } - url = 'https://doi.crossref.org/servlet/getForwardLinks?{}'.format( + url = "https://doi.crossref.org/servlet/getForwardLinks?{}".format( urlencode(payload) ) print(url) - print('Making request.', end='... ') + print("Making request.", end="... ") r = requests.get(url) - print('[ok]') + print("[ok]") - print('Souping response', end='... ') - soup = BeautifulSoup(r.text, 'lxml') - print('[ok]') + print("Souping response", end="... ") + soup = BeautifulSoup(r.text, "lxml") + print("[ok]") - print('Finding forward links', end='... ') - forward_links = soup.find_all('forward_link') - print('[ok]') + print("Finding forward links", end="... ") + forward_links = soup.find_all("forward_link") + print("[ok]") - print('Looping through links', end='... ') + print("Looping through links", end="... ") for link in forward_links: - type = link.doi.get('type') - doi = link.get('doi') + type = link.doi.get("type") + doi = link.get("doi") article = sm.Article.get_article( journal, - 'doi', + "doi", doi, ) if article: - if type == 'journal_article': + if type == "journal_article": process_article(link, article) - elif type == 'book_content': + elif type == "book_content": process_book(link, article) else: - print('Forward link type {} not supported'.format(type)) + print("Forward link type {} not supported".format(type)) else: - print('Article with doi {} not found.'.format(doi)) + print("Article with doi {} not found.".format(doi)) print(len(forward_links)) diff --git a/src/metrics/management/commands/process_crossref_events.py b/src/metrics/management/commands/process_crossref_events.py index 6af19bfbbc..54c89a29dd 100755 --- a/src/metrics/management/commands/process_crossref_events.py +++ b/src/metrics/management/commands/process_crossref_events.py @@ -13,32 +13,38 @@ def process_events(): - for journal in journal_models.Journal.objects.all(): if journal.use_crossref: - crossref_prefix = journal.get_setting('Identifiers', 'crossref_prefix') - - file_path = os.path.join(settings.BASE_DIR, 'files', 'temp', '{prefix}.json'.format(prefix=crossref_prefix)) - with open(file_path, encoding='utf-8') as json_file: + crossref_prefix = journal.get_setting("Identifiers", "crossref_prefix") + + file_path = os.path.join( + settings.BASE_DIR, + "files", + "temp", + "{prefix}.json".format(prefix=crossref_prefix), + ) + with open(file_path, encoding="utf-8") as json_file: json_data = json.loads(json_file.read()) - for event in json_data['message']['events']: - obj_id_parts = event['obj_id'].split('/') - doi = '{0}/{1}'.format(obj_id_parts[3], obj_id_parts[4]) + for event in json_data["message"]["events"]: + obj_id_parts = event["obj_id"].split("/") + doi = "{0}/{1}".format(obj_id_parts[3], obj_id_parts[4]) try: - identifier = ident_models.Identifier.objects.get(id_type='doi', identifier=doi) - print('{0} found.'.format(doi)) - print(event['subj']['pid']) + identifier = ident_models.Identifier.objects.get( + id_type="doi", identifier=doi + ) + print("{0} found.".format(doi)) + print(event["subj"]["pid"]) try: models.AltMetric.objects.get_or_create( article=identifier.article, - source=event['source_id'], - pid=event['subj']['pid'], - timestamp=event['timestamp'], + source=event["source_id"], + pid=event["subj"]["pid"], + timestamp=event["timestamp"], ) except IntegrityError: - print('Duplicate found, skipping.') + print("Duplicate found, skipping.") except ident_models.Identifier.DoesNotExist: # This doi isn't found pass # print('{0} not found.'.format(doi)) @@ -46,26 +52,29 @@ def process_events(): def fetch_crossref_data(): journal_prefixes = list() - temp_folder = os.path.join(settings.BASE_DIR, 'files', 'temp') + temp_folder = os.path.join(settings.BASE_DIR, "files", "temp") for journal in journal_models.Journal.objects.all(): if journal.use_crossref: try: - crossref_prefix = journal.get_setting('Identifiers', 'crossref_prefix') + crossref_prefix = journal.get_setting("Identifiers", "crossref_prefix") journal_prefixes.append(crossref_prefix) except IndexError: - print('{0} has no DOI Prefix set.'.format(journal)) + print("{0} has no DOI Prefix set.".format(journal)) journal_prefixes = set(journal_prefixes) for prefix in journal_prefixes: - file_path = os.path.join(temp_folder, '{prefix}.json'.format(prefix=prefix)) + file_path = os.path.join(temp_folder, "{prefix}.json".format(prefix=prefix)) if not os.path.isfile(file_path): r = requests.get( - 'https://query.eventdata.crossref.org/events?rows=10000&filter=obj-id.prefix:{0}'.format(prefix)) + "https://query.eventdata.crossref.org/events?rows=10000&filter=obj-id.prefix:{0}".format( + prefix + ) + ) - with open(file_path, 'w') as file: + with open(file_path, "w") as file: file.write(json.dumps(r.json())) @@ -77,12 +86,12 @@ class Command(BaseCommand): help = "Import Crossref Events into local models." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--dump_data', action='store_true', default=False) + parser.add_argument("--dump_data", action="store_true", default=False) def handle(self, *args, **options): """Collects Crossref Events, parses them and stores new events locally. @@ -94,18 +103,16 @@ def handle(self, *args, **options): translation.activate(settings.LANGUAGE_CODE) - file_name = '{date}.json'.format(date=timezone.localdate()) - file_path = os.path.join(settings.BASE_DIR, 'files', 'temp', file_name) + file_name = "{date}.json".format(date=timezone.localdate()) + file_path = os.path.join(settings.BASE_DIR, "files", "temp", file_name) if os.path.isfile(file_path): - # Process file - print('Existing file found.') + print("Existing file found.") process_events() else: - # Fetch data - print('Fetching data from crossref event tracking API.') + print("Fetching data from crossref event tracking API.") fetch_crossref_data() process_events() diff --git a/src/metrics/management/commands/reload_access_dump.py b/src/metrics/management/commands/reload_access_dump.py index 48a1b13414..1b5fead175 100755 --- a/src/metrics/management/commands/reload_access_dump.py +++ b/src/metrics/management/commands/reload_access_dump.py @@ -10,15 +10,17 @@ class Command(BaseCommand): A management command that reloads a dumped access list and removes the totals from the historic views. """ - help = "Reloads a dumped access list and removes the totals from the historic views." + help = ( + "Reloads a dumped access list and removes the totals from the historic views." + ) def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--file', default=False) + parser.add_argument("--file", default=False) def handle(self, *args, **options): """Reloads a dumped access list and removes the totals from the historic views. @@ -28,11 +30,13 @@ def handle(self, *args, **options): :return: None """ - file_path = os.path.join(settings.BASE_DIR, - 'files', - 'data_backup', - options.get('file'), - 'article_accesses.json') + file_path = os.path.join( + settings.BASE_DIR, + "files", + "data_backup", + options.get("file"), + "article_accesses.json", + ) with open(file_path, encoding="utf-8") as f: data = f.read() @@ -40,7 +44,7 @@ def handle(self, *args, **options): for obj in serializers.deserialize("json", data): obj.save() - if obj.object.type == 'view': + if obj.object.type == "view": obj.object.article.historicarticleaccess.remove_one_view() - elif obj.object.type == 'download': + elif obj.object.type == "download": obj.object.article.historicarticleaccess.remove_one_download() diff --git a/src/metrics/migrations/0001_initial.py b/src/metrics/migrations/0001_initial.py index 24bc9bf822..38146ae22f 100755 --- a/src/metrics/migrations/0001_initial.py +++ b/src/metrics/migrations/0001_initial.py @@ -8,32 +8,65 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('submission', '0001_initial'), + ("submission", "0001_initial"), ] operations = [ migrations.CreateModel( - name='ArticleAccess', + name="ArticleAccess", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('download', 'Download'), ('view', 'View')], max_length=20)), - ('identifier', models.CharField(max_length=200)), - ('accessed', models.DateTimeField(default=django.utils.timezone.now)), - ('galley_type', models.CharField(max_length=200)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.CharField( + choices=[("download", "Download"), ("view", "View")], + max_length=20, + ), + ), + ("identifier", models.CharField(max_length=200)), + ("accessed", models.DateTimeField(default=django.utils.timezone.now)), + ("galley_type", models.CharField(max_length=200)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], ), migrations.CreateModel( - name='HistoricArticleAccess', + name="HistoricArticleAccess", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('views', models.PositiveIntegerField(default=0)), - ('downloads', models.PositiveIntegerField(default=0)), - ('article', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("views", models.PositiveIntegerField(default=0)), + ("downloads", models.PositiveIntegerField(default=0)), + ( + "article", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], ), ] diff --git a/src/metrics/migrations/0002_altmetric.py b/src/metrics/migrations/0002_altmetric.py index 39429dcb7b..d350af8975 100755 --- a/src/metrics/migrations/0002_altmetric.py +++ b/src/metrics/migrations/0002_altmetric.py @@ -7,21 +7,52 @@ class Migration(migrations.Migration): - dependencies = [ - ('identifiers', '0002_auto_20170711_1203'), - ('metrics', '0001_initial'), + ("identifiers", "0002_auto_20170711_1203"), + ("metrics", "0001_initial"), ] operations = [ migrations.CreateModel( - name='AltMetric', + name="AltMetric", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('source', models.CharField(choices=[('twitter', 'Twitter'), ('crossref', 'Crossref'), ('datacite', 'DataCite'), ('reddit', 'Reddit'), ('reddit-links', 'Reddit Links'), ('hypothesis', 'Hypothesis'), ('newsfeed', 'News'), ('stackexchange', 'Stack Exchange'), ('web', 'Web'), ('wikipedia', 'Wikipedia'), ('wordpressdotcom', 'Wordpress')], max_length=30)), - ('pid', models.TextField()), - ('timestamp', models.DateTimeField()), - ('identifier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='identifiers.Identifier')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "source", + models.CharField( + choices=[ + ("twitter", "Twitter"), + ("crossref", "Crossref"), + ("datacite", "DataCite"), + ("reddit", "Reddit"), + ("reddit-links", "Reddit Links"), + ("hypothesis", "Hypothesis"), + ("newsfeed", "News"), + ("stackexchange", "Stack Exchange"), + ("web", "Web"), + ("wikipedia", "Wikipedia"), + ("wordpressdotcom", "Wordpress"), + ], + max_length=30, + ), + ), + ("pid", models.TextField()), + ("timestamp", models.DateTimeField()), + ( + "identifier", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="identifiers.Identifier", + ), + ), ], ), ] diff --git a/src/metrics/migrations/0003_auto_20170921_0937.py b/src/metrics/migrations/0003_auto_20170921_0937.py index cdecf66f71..4bd2de3bd7 100755 --- a/src/metrics/migrations/0003_auto_20170921_0937.py +++ b/src/metrics/migrations/0003_auto_20170921_0937.py @@ -7,21 +7,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0011_auto_20170921_0937'), - ('metrics', '0002_altmetric'), + ("submission", "0011_auto_20170921_0937"), + ("metrics", "0002_altmetric"), ] operations = [ migrations.RemoveField( - model_name='altmetric', - name='identifier', + model_name="altmetric", + name="identifier", ), migrations.AddField( - model_name='altmetric', - name='article', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="altmetric", + name="article", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), preserve_default=False, ), ] diff --git a/src/metrics/migrations/0004_auto_20180308_1732.py b/src/metrics/migrations/0004_auto_20180308_1732.py index edb0065c39..6f2fc4c92e 100644 --- a/src/metrics/migrations/0004_auto_20180308_1732.py +++ b/src/metrics/migrations/0004_auto_20180308_1732.py @@ -6,20 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), - ('metrics', '0003_auto_20170921_0937'), + ("submission", "0019_auto_20171130_1115"), + ("metrics", "0003_auto_20170921_0937"), ] operations = [ migrations.AlterField( - model_name='altmetric', - name='pid', + model_name="altmetric", + name="pid", field=models.CharField(max_length=200), ), migrations.AlterUniqueTogether( - name='altmetric', - unique_together=set([('article', 'source', 'pid')]), + name="altmetric", + unique_together=set([("article", "source", "pid")]), ), ] diff --git a/src/metrics/migrations/0005_articleaccess_country.py b/src/metrics/migrations/0005_articleaccess_country.py index 8b187952d8..676b0ba4ab 100644 --- a/src/metrics/migrations/0005_articleaccess_country.py +++ b/src/metrics/migrations/0005_articleaccess_country.py @@ -7,16 +7,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0030_merge_20190405_1549'), - ('metrics', '0004_auto_20180308_1732'), + ("core", "0030_merge_20190405_1549"), + ("metrics", "0004_auto_20180308_1732"), ] operations = [ migrations.AddField( - model_name='articleaccess', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Country'), + model_name="articleaccess", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Country", + ), ), ] diff --git a/src/metrics/migrations/0006_auto_20190627_1412.py b/src/metrics/migrations/0006_auto_20190627_1412.py index 3d3c9ba627..fc8cbb4fce 100644 --- a/src/metrics/migrations/0006_auto_20190627_1412.py +++ b/src/metrics/migrations/0006_auto_20190627_1412.py @@ -8,8 +8,8 @@ def process_accesses(apps, schema_editor): - ArticleAccess = apps.get_model('metrics', 'ArticleAccess') - Country = apps.get_model('core', 'Country') + ArticleAccess = apps.get_model("metrics", "ArticleAccess") + Country = apps.get_model("core", "Country") accesses = ArticleAccess.objects.all() @@ -31,12 +31,10 @@ def process_accesses(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('metrics', '0005_articleaccess_country'), + ("metrics", "0005_articleaccess_country"), ] operations = [ - migrations.RunPython(process_accesses, - reverse_code=migrations.RunPython.noop) + migrations.RunPython(process_accesses, reverse_code=migrations.RunPython.noop) ] diff --git a/src/metrics/migrations/0007_articlelink_booklink.py b/src/metrics/migrations/0007_articlelink_booklink.py index 03adb02ea9..8017fbe0ee 100644 --- a/src/metrics/migrations/0007_articlelink_booklink.py +++ b/src/metrics/migrations/0007_articlelink_booklink.py @@ -7,46 +7,89 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0039_auto_20191115_1253'), - ('metrics', '0006_auto_20190627_1412'), + ("submission", "0039_auto_20191115_1253"), + ("metrics", "0006_auto_20190627_1412"), ] operations = [ migrations.CreateModel( - name='ArticleLink', + name="ArticleLink", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('doi', models.CharField(max_length=255)), - ('object_type', models.CharField(choices=[('book', 'Book'), ('article', 'Article')], max_length=10)), - ('year', models.CharField(max_length=5)), - ('journal_title', models.TextField()), - ('journal_issn', models.CharField(max_length=20)), - ('article_title', models.TextField()), - ('volume', models.CharField(blank=True, max_length=10, null=True)), - ('issue', models.CharField(blank=True, max_length=10, null=True)), - ('article', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("doi", models.CharField(max_length=255)), + ( + "object_type", + models.CharField( + choices=[("book", "Book"), ("article", "Article")], + max_length=10, + ), + ), + ("year", models.CharField(max_length=5)), + ("journal_title", models.TextField()), + ("journal_issn", models.CharField(max_length=20)), + ("article_title", models.TextField()), + ("volume", models.CharField(blank=True, max_length=10, null=True)), + ("issue", models.CharField(blank=True, max_length=10, null=True)), + ( + "article", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='BookLink', + name="BookLink", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('doi', models.CharField(max_length=255)), - ('object_type', models.CharField(choices=[('book', 'Book'), ('article', 'Article')], max_length=10)), - ('year', models.CharField(max_length=5)), - ('title', models.TextField()), - ('isbn_print', models.TextField()), - ('isbn_electronic', models.TextField()), - ('component_number', models.TextField()), - ('article', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("doi", models.CharField(max_length=255)), + ( + "object_type", + models.CharField( + choices=[("book", "Book"), ("article", "Article")], + max_length=10, + ), + ), + ("year", models.CharField(max_length=5)), + ("title", models.TextField()), + ("isbn_print", models.TextField()), + ("isbn_electronic", models.TextField()), + ("component_number", models.TextField()), + ( + "article", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/src/metrics/migrations/0008_auto_20191119_2340.py b/src/metrics/migrations/0008_auto_20191119_2340.py index 5e14541a11..076b79b7a5 100644 --- a/src/metrics/migrations/0008_auto_20191119_2340.py +++ b/src/metrics/migrations/0008_auto_20191119_2340.py @@ -6,20 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('metrics', '0007_articlelink_booklink'), + ("metrics", "0007_articlelink_booklink"), ] operations = [ migrations.AlterField( - model_name='articlelink', - name='year', + model_name="articlelink", + name="year", field=models.PositiveIntegerField(), ), migrations.AlterField( - model_name='booklink', - name='year', + model_name="booklink", + name="year", field=models.PositiveIntegerField(), ), ] diff --git a/src/metrics/migrations/0009_fix_metrics_galley_type.py b/src/metrics/migrations/0009_fix_metrics_galley_type.py index bfdacc6076..db3142143e 100644 --- a/src/metrics/migrations/0009_fix_metrics_galley_type.py +++ b/src/metrics/migrations/0009_fix_metrics_galley_type.py @@ -6,10 +6,10 @@ def fix_bad_galley_type(apps, schema_editor): - """ Fixes records that have mistakenly set their galley_type as 'view' """ + """Fixes records that have mistakenly set their galley_type as 'view'""" ArticleAccess = apps.get_model("metrics", "ArticleAccess") Article = apps.get_model("submission", "Article") - for galley_type in ["html", "xml", "image"]: # Order matters + for galley_type in ["html", "xml", "image"]: # Order matters print("Fixing Access records of type '%s'" % galley_type) # First, fix articles with no render_galley: articles = Article.objects.filter( @@ -41,15 +41,14 @@ def rollback(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('metrics', '0008_auto_20191119_2340'), + ("metrics", "0008_auto_20191119_2340"), ] operations = [ migrations.AlterField( - model_name='articleaccess', - name='galley_type', + model_name="articleaccess", + name="galley_type", field=models.CharField(blank=True, max_length=200, null=True), ), migrations.RunPython( diff --git a/src/metrics/migrations/0010_auto_20230317_1534.py b/src/metrics/migrations/0010_auto_20230317_1534.py index 5efad9ebfe..e6093d2346 100644 --- a/src/metrics/migrations/0010_auto_20230317_1534.py +++ b/src/metrics/migrations/0010_auto_20230317_1534.py @@ -5,24 +5,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0081_alter_account_preferred_timezone'), - ('metrics', '0009_fix_metrics_galley_type'), + ("core", "0081_alter_account_preferred_timezone"), + ("metrics", "0009_fix_metrics_galley_type"), ] operations = [ migrations.AlterModelOptions( - name='articleaccess', - options={'verbose_name_plural': 'article access events'}, + name="articleaccess", + options={"verbose_name_plural": "article access events"}, ), migrations.AlterModelOptions( - name='historicarticleaccess', - options={'verbose_name_plural': 'historic article access events'}, + name="historicarticleaccess", + options={"verbose_name_plural": "historic article access events"}, ), migrations.AlterField( - model_name='articleaccess', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.country'), + model_name="articleaccess", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.country", + ), ), ] diff --git a/src/metrics/models.py b/src/metrics/models.py index 09377dfaa8..52ef3c92c9 100755 --- a/src/metrics/models.py +++ b/src/metrics/models.py @@ -9,14 +9,14 @@ def access_choices(): return ( - ('download', 'Download'), - ('view', 'View'), + ("download", "Download"), + ("view", "View"), ) class ArticleAccess(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) type = models.CharField(max_length=20, choices=access_choices()) @@ -24,32 +24,36 @@ class ArticleAccess(models.Model): accessed = models.DateTimeField(default=timezone.now) galley_type = models.CharField(max_length=200, null=True, blank=True) country = models.ForeignKey( - 'core.Country', + "core.Country", blank=True, null=True, on_delete=models.SET_NULL, ) class Meta: - verbose_name_plural = 'article access events' + verbose_name_plural = "article access events" def __str__(self): - return '[{0}] - {1} at {2}'.format(self.identifier, self.article.title, self.accessed) + return "[{0}] - {1} at {2}".format( + self.identifier, self.article.title, self.accessed + ) class HistoricArticleAccess(models.Model): article = models.OneToOneField( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) views = models.PositiveIntegerField(default=0) downloads = models.PositiveIntegerField(default=0) class Meta: - verbose_name_plural = 'historic article access events' + verbose_name_plural = "historic article access events" def __str__(self): - return 'Article {0}, Views: {1}, Downloads: {2}'.format(self.article.title, self.views, self.downloads) + return "Article {0}, Views: {1}, Downloads: {2}".format( + self.article.title, self.views, self.downloads + ) def add_one_view(self): views = self.views @@ -74,14 +78,14 @@ def remove_one_download(self): def object_types(): return ( - ('book', 'Book'), - ('article', 'Article'), + ("book", "Book"), + ("article", "Article"), ) class AbstractForwardLink(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.CASCADE, @@ -102,9 +106,7 @@ class ArticleLink(AbstractForwardLink): issue = models.CharField(max_length=10, blank=True, null=True) def __str__(self): - return 'Article Link: {}'.format( - self.article_title - ) + return "Article Link: {}".format(self.article_title) class BookLink(AbstractForwardLink): @@ -114,31 +116,28 @@ class BookLink(AbstractForwardLink): component_number = models.TextField() def __str__(self): - return 'Book Link: {}'.format( - self.title - ) + return "Book Link: {}".format(self.title) def alt_metric_choices(): return ( - ('twitter', 'Twitter'), - ('crossref', 'Crossref'), - ('datacite', 'DataCite'), - ('reddit', 'Reddit'), - ('reddit-links', 'Reddit Links'), - ('hypothesis', 'Hypothesis'), - ('newsfeed', 'News'), - ('stackexchange', 'Stack Exchange'), - ('web', 'Web'), - ('wikipedia', 'Wikipedia'), - ('wordpressdotcom', 'Wordpress'), - + ("twitter", "Twitter"), + ("crossref", "Crossref"), + ("datacite", "DataCite"), + ("reddit", "Reddit"), + ("reddit-links", "Reddit Links"), + ("hypothesis", "Hypothesis"), + ("newsfeed", "News"), + ("stackexchange", "Stack Exchange"), + ("web", "Web"), + ("wikipedia", "Wikipedia"), + ("wordpressdotcom", "Wordpress"), ) class AltMetric(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) source = models.CharField(max_length=30, choices=alt_metric_choices()) @@ -146,4 +145,4 @@ class AltMetric(models.Model): timestamp = models.DateTimeField() class Meta: - unique_together = ('article', 'source', 'pid') + unique_together = ("article", "source", "pid") diff --git a/src/metrics/tests.py b/src/metrics/tests.py index 35b9ce7276..50311b5eda 100644 --- a/src/metrics/tests.py +++ b/src/metrics/tests.py @@ -11,8 +11,8 @@ from utils.testing import helpers from utils.shared import clear_cache -class ArticleAccessTests(TestCase): +class ArticleAccessTests(TestCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -35,7 +35,7 @@ def test_article_access_when_view_abstract(self): self.client.get( self.article_url, SERVER_NAME=self.journal_one.domain, - HTTP_USER_AGENT='Chrome/39.0.2171.95 Safari/537.36', + HTTP_USER_AGENT="Chrome/39.0.2171.95 Safari/537.36", follow=True, ) self.assertTrue( @@ -44,21 +44,19 @@ def test_article_access_when_view_abstract(self): type="view", galley_type=None, ).exists(), - "A 'view' has not been recorded for abstract when no render galley" + "A 'view' has not been recorded for abstract when no render galley", ) def test_article_access_when_view_render_galley(self): galley_type = "html" - galley = helpers.create_galley( - self.article, type=galley_type, public=True - ) + galley = helpers.create_galley(self.article, type=galley_type, public=True) self.article.render_galley = galley self.article.save() self.client.get( self.article_url, SERVER_NAME=self.journal_one.domain, - HTTP_USER_AGENT='Chrome/39.0.2171.95 Safari/537.36', + HTTP_USER_AGENT="Chrome/39.0.2171.95 Safari/537.36", follow=True, ) self.assertTrue( @@ -67,20 +65,18 @@ def test_article_access_when_view_render_galley(self): type="view", galley_type=galley_type, ).exists(), - "A 'view' has not been recorded for rendered galley" + "A 'view' has not been recorded for rendered galley", ) def test_NO_article_access_when_view_non_render_galley(self): galley_type = "pdf" - galley = helpers.create_galley( - self.article, type=galley_type, public=True - ) + galley = helpers.create_galley(self.article, type=galley_type, public=True) self.article.save() self.client.get( self.article_url, SERVER_NAME=self.journal_one.domain, - HTTP_USER_AGENT='Chrome/39.0.2171.95 Safari/537.36', + HTTP_USER_AGENT="Chrome/39.0.2171.95 Safari/537.36", follow=True, ) self.assertFalse( @@ -89,7 +85,7 @@ def test_NO_article_access_when_view_non_render_galley(self): type="view", galley_type=galley_type, ).exists(), - "A 'view' has mistakenly been recorded against a non-render galley" + "A 'view' has mistakenly been recorded against a non-render galley", ) self.assertTrue( ArticleAccess.objects.filter( @@ -97,20 +93,18 @@ def test_NO_article_access_when_view_non_render_galley(self): type="view", galley_type=None, ).exists(), - "A 'view' has not been recorded for abstract when no render galley" + "A 'view' has not been recorded for abstract when no render galley", ) def test_article_access_when_download_galley(self): galley_type = "pdf" - galley = helpers.create_galley( - self.article, type=galley_type, public=True - ) + galley = helpers.create_galley(self.article, type=galley_type, public=True) - galley_url = f'/article/{self.article.pk}/galley/{galley.pk}/download/' + galley_url = f"/article/{self.article.pk}/galley/{galley.pk}/download/" self.client.get( galley_url, SERVER_NAME=self.journal_one.domain, - HTTP_USER_AGENT='Chrome/39.0.2171.95 Safari/537.36', + HTTP_USER_AGENT="Chrome/39.0.2171.95 Safari/537.36", follow=True, ) self.assertTrue( @@ -119,5 +113,5 @@ def test_article_access_when_download_galley(self): type="download", galley_type=galley_type, ).exists(), - "No 'download' recorded when downloading PDF galley" + "No 'download' recorded when downloading PDF galley", ) diff --git a/src/metrics/urls.py b/src/metrics/urls.py index c193e963ab..4d3e5cebc3 100755 --- a/src/metrics/urls.py +++ b/src/metrics/urls.py @@ -7,22 +7,21 @@ from metrics import views urlpatterns = [ - - re_path(r'^sushi/$', - views.sushi, - name='sushi'), - + re_path(r"^sushi/$", views.sushi, name="sushi"), # NB to avoid denial of service attacks, we do not allow requests to customize the date range - re_path(r'^counter/reports/jr1/(?P.+)/$', + re_path( + r"^counter/reports/jr1/(?P.+)/$", views.jr_one_no_date, - name='journal_report_one_no_date'), - - re_path(r'^counter/reports/jr1_goa/(?P.+)/$', + name="journal_report_one_no_date", + ), + re_path( + r"^counter/reports/jr1_goa/(?P.+)/$", views.jr_one_goa_no_date, - name='journal_report_one_goa_no_date'), - - re_path(r'^counter/reports/jr2/(?P.+)/$', + name="journal_report_one_goa_no_date", + ), + re_path( + r"^counter/reports/jr2/(?P.+)/$", views.jr_two_no_date, - name='journal_report_two_no_date') - + name="journal_report_two_no_date", + ), ] diff --git a/src/metrics/views.py b/src/metrics/views.py index e10eba72be..51e77a8feb 100755 --- a/src/metrics/views.py +++ b/src/metrics/views.py @@ -41,68 +41,178 @@ def sushi(request): # \n \n 2015-01-01\n # 2017-01-31\n \n \n # \n \n \n\n' - body = BeautifulSoup(request.body, 'xml') + body = BeautifulSoup(request.body, "xml") # TODO: pass through parameters like the requestor ID etc. and echo them back in the XML try: - report_type = body.find('ReportDefinition').attrs['Name'] - report_request_id = body.find('ReportRequest').attrs['ID'] + report_type = body.find("ReportDefinition").attrs["Name"] + report_request_id = body.find("ReportRequest").attrs["ID"] - requestor = body.find('Requestor') - requestor_id = requestor.find('ID').text - requestor_email = requestor.find('Email').text - requestor_name = requestor.find('Name').text + requestor = body.find("Requestor") + requestor_id = requestor.find("ID").text + requestor_email = requestor.find("Email").text + requestor_name = requestor.find("Name").text - customer = body.find('CustomerReference') - customer_ID = customer.find('ID').text - customer_name = customer.find('Name').text + customer = body.find("CustomerReference") + customer_ID = customer.find("ID").text + customer_name = customer.find("Name").text except AttributeError: - return HttpResponse(json.dumps({'Error': 'Malformed request.'})) - - if report_type.upper() == 'JR1': - return jr_one_no_date(request, 'xml', compat=True, requestor_id=requestor_id, requestor_email=requestor_email, - requestor_name=requestor_name, customer_ID=customer_ID, customer_name=customer_name, - report_request_id=report_request_id) - elif report_type.upper() == 'JR1GOA': - return jr_one_goa_no_date(request, 'xml', compat=True, requestor_id=requestor_id, - requestor_email=requestor_email, requestor_name=requestor_name, - customer_ID=customer_ID, customer_name=customer_name, - report_request_id=report_request_id) - elif report_type.upper() == 'JR2': - return jr_two_no_date(request, 'xml', compat=True, requestor_id=requestor_id, requestor_email=requestor_email, - requestor_name=requestor_name, customer_ID=customer_ID, customer_name=customer_name, - report_request_id=report_request_id) - - -def jr_one_no_date(request, output_format, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): + return HttpResponse(json.dumps({"Error": "Malformed request."})) + + if report_type.upper() == "JR1": + return jr_one_no_date( + request, + "xml", + compat=True, + requestor_id=requestor_id, + requestor_email=requestor_email, + requestor_name=requestor_name, + customer_ID=customer_ID, + customer_name=customer_name, + report_request_id=report_request_id, + ) + elif report_type.upper() == "JR1GOA": + return jr_one_goa_no_date( + request, + "xml", + compat=True, + requestor_id=requestor_id, + requestor_email=requestor_email, + requestor_name=requestor_name, + customer_ID=customer_ID, + customer_name=customer_name, + report_request_id=report_request_id, + ) + elif report_type.upper() == "JR2": + return jr_two_no_date( + request, + "xml", + compat=True, + requestor_id=requestor_id, + requestor_email=requestor_email, + requestor_name=requestor_name, + customer_ID=customer_ID, + customer_name=customer_name, + report_request_id=report_request_id, + ) + + +def jr_one_no_date( + request, + output_format, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): start_date = timezone.now() - relativedelta(years=2) - return jr_one_no_end_date(request, output_format, start_date.year, start_date.month, start_date.day, compat, - report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_one_no_end_date(request, output_format, start_year, start_month, start_day, compat=False, report_request_id='', - requestor_id='', requestor_email='', requestor_name='', customer_ID='', customer_name=''): - return jr_one_all_dates(request, output_format, start_year, start_month, start_day, timezone.now().year, - timezone.now().month, timezone.now().day, compat, report_request_id, requestor_id, - requestor_email, requestor_name, customer_ID, customer_name) - - -def jr_one_all_dates(request, output_format, start_year, start_month, start_day, end_year, end_month, end_day, - compat=False, report_request_id='', requestor_id='', requestor_email='', requestor_name='', - customer_ID='', customer_name=''): - return jr_one(request, output_format, - timezone.datetime(int(start_year), int(start_month), int(start_day), - tzinfo=timezone.get_current_timezone()), - timezone.datetime(int(end_year), int(end_month), int(end_day), - tzinfo=timezone.get_current_timezone()), - compat, report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_one(request, output_format, start_date, end_date, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): + return jr_one_no_end_date( + request, + output_format, + start_date.year, + start_date.month, + start_date.day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one_no_end_date( + request, + output_format, + start_year, + start_month, + start_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_one_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + timezone.now().year, + timezone.now().month, + timezone.now().day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + end_year, + end_month, + end_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_one( + request, + output_format, + timezone.datetime( + int(start_year), + int(start_month), + int(start_day), + tzinfo=timezone.get_current_timezone(), + ), + timezone.datetime( + int(end_year), + int(end_month), + int(end_day), + tzinfo=timezone.get_current_timezone(), + ), + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one( + request, + output_format, + start_date, + end_date, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): """ Produces COUNTER R1 report in either TSV or XML formats :param request: the request object @@ -116,43 +226,67 @@ def jr_one(request, output_format, start_date, end_date, compat=False, report_re press_object = press.models.Press.get_press(request) # fetch the metrics - press_total, press_views, press_downloads, press_months, journals = logic.get_press_totals(start_date, - end_date, - report_months, - compat=compat) + press_total, press_views, press_downloads, press_months, journals = ( + logic.get_press_totals(start_date, end_date, report_months, compat=compat) + ) - if output_format.upper() == 'TSV': + if output_format.upper() == "TSV": # output a TSV report - return j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_total, press_views, - report_months, request, press_object, 'Journal Report 1(R4)', - 'Number of Successful Full-text Article Requests by Month and Journal') + return j1_tsv( + start_date, + end_date, + journals, + press_downloads, + press_months, + press_total, + press_views, + report_months, + request, + press_object, + "Journal Report 1(R4)", + "Number of Successful Full-text Article Requests by Month and Journal", + ) else: # output an XML report - template = 'metrics/counter_jr1.xml' + template = "metrics/counter_jr1.xml" # SUSHI COUNTER spec at: http://www.niso.org/apps/group_public/download.php/14101/COUNTER4_1.png # unknown variables: # vendor_id - context = {'start_date': start_date.strftime('%Y-%m-%d'), - 'end_date': end_date.strftime('%Y-%m-%d'), - 'report_items': journals, - 'report_created': timezone.now(), - 'vendor_name': press_object.name, - 'item_publisher': press_object.name, - 'vendor_email': press_object.main_contact, - 'report_ID': report_request_id, - 'requestor_ID': requestor_id, - 'requestor_name': requestor_name, - 'requestor_email': requestor_email, - 'customer_ID': customer_ID, - 'customer_name': customer_name} - - return render(request, template, context, content_type='text/xml') - - -def j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_total, press_views, report_months, - request, press_object, report_title, report_description): + context = { + "start_date": start_date.strftime("%Y-%m-%d"), + "end_date": end_date.strftime("%Y-%m-%d"), + "report_items": journals, + "report_created": timezone.now(), + "vendor_name": press_object.name, + "item_publisher": press_object.name, + "vendor_email": press_object.main_contact, + "report_ID": report_request_id, + "requestor_ID": requestor_id, + "requestor_name": requestor_name, + "requestor_email": requestor_email, + "customer_ID": customer_ID, + "customer_name": customer_name, + } + + return render(request, template, context, content_type="text/xml") + + +def j1_tsv( + start_date, + end_date, + journals, + press_downloads, + press_months, + press_total, + press_views, + report_months, + request, + press_object, + report_title, + report_description, +): """ COUNTER SPEC: @@ -195,65 +329,79 @@ def j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_ Similarly, Cell L10 down to Cell L[n], Cell M10 down to Cell M[n] etc. contain the Full Text Requests for the corresponding months Cell H9 and Cell K9 across to Cell M7 (or whatever column corresponds to the last column of the table) gives totals for each column. The figure reported in these cells in Row 9 must equal the sum of the cells for that column from Row 10 to the bottom of the table. """ - row_one, row_two, row_three, row_four, row_five, row_six, row_seven = create_tsv_header(request, - start_date, - end_date, - report_title, - report_description, - press_object) + row_one, row_two, row_three, row_four, row_five, row_six, row_seven = ( + create_tsv_header( + request, + start_date, + end_date, + report_title, + report_description, + press_object, + ) + ) row_eight = [] row_nine = [] - rows = [row_one, row_two, row_three, row_four, row_five, row_six, row_seven, row_eight, row_nine] + rows = [ + row_one, + row_two, + row_three, + row_four, + row_five, + row_six, + row_seven, + row_eight, + row_nine, + ] # A8 - row_eight.append('Journal') + row_eight.append("Journal") # B8 - row_eight.append('Publisher') + row_eight.append("Publisher") # C8 - row_eight.append('Platform') + row_eight.append("Platform") # D8 - row_eight.append('Journal DOI') + row_eight.append("Journal DOI") # E8 - row_eight.append('Proprietary Identifier') + row_eight.append("Proprietary Identifier") # F8 - row_eight.append('Print ISSN') + row_eight.append("Print ISSN") # G8 - row_eight.append('Online ISSN') + row_eight.append("Online ISSN") # H8 # The game, not the player. - row_eight.append('Reporting Period Total') + row_eight.append("Reporting Period Total") # I8 - row_eight.append('Reporting Period HTML') + row_eight.append("Reporting Period HTML") # J8 - row_eight.append('Reporting Period PDF') + row_eight.append("Reporting Period PDF") # K8 -> [X]8 for date in report_months: - row_eight.append('{0}-{1}'.format(date.strftime('%b'), date.year)) + row_eight.append("{0}-{1}".format(date.strftime("%b"), date.year)) # A9 - row_nine.append('Total for all Journals') + row_nine.append("Total for all Journals") # B9 row_nine.append(press_object.name) # C9 - row_nine.append('Janeway') + row_nine.append("Janeway") # D9 -> G9 for x in range(1, 5): - row_nine.append('') + row_nine.append("") # H9 row_nine.append(press_total) @@ -266,24 +414,36 @@ def j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_ # K9 -> [X]9 for date in report_months: - row_nine.append(press_months['{0}-{1}'.format(date.strftime('%b'), date.year)]) + row_nine.append(press_months["{0}-{1}".format(date.strftime("%b"), date.year)]) for journal_dict in journals: - journal = journal_dict['journal'] - journal_row = [journal.name, press_object.name, 'Janeway', '', '', '', journal.issn, - journal_dict['total'], journal_dict['total_views'], journal_dict['total_downloads']] + journal = journal_dict["journal"] + journal_row = [ + journal.name, + press_object.name, + "Janeway", + "", + "", + "", + journal.issn, + journal_dict["total"], + journal_dict["total_views"], + journal_dict["total_downloads"], + ] for date in report_months: - journal_row.append(journal_dict['{0}-{1}'.format(date.strftime('%b'), date.year)]) + journal_row.append( + journal_dict["{0}-{1}".format(date.strftime("%b"), date.year)] + ) rows.append(journal_row) # Create the HttpResponse object with the appropriate TSV header. - response = HttpResponse(content_type='text/tsv') - response['Content-Disposition'] = 'attachment; filename="JR1.tsv"' + response = HttpResponse(content_type="text/tsv") + response["Content-Disposition"] = 'attachment; filename="JR1.tsv"' # output the TSV - writer = csv.writer(response, delimiter='\t') + writer = csv.writer(response, delimiter="\t") for row in rows: writer.writerow(row) @@ -291,7 +451,9 @@ def j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_ return response -def create_tsv_header(request, start_date, end_date, report_title, report_description, press_object): +def create_tsv_header( + request, start_date, end_date, report_title, report_description, press_object +): row_one = [] row_two = [] row_three = [] @@ -318,59 +480,154 @@ def create_tsv_header(request, start_date, end_date, report_title, report_descri # We do not use institutional identifiers # A3 - row_three.append('') + row_three.append("") # A4 - row_four.append('Period covered by Report') + row_four.append("Period covered by Report") # A5 - row_five.append('{0}-{1}-{2} to {3}-{4}-{5}'.format(start_date.year, - start_date.strftime('%m'), - start_date.strftime('%d'), - end_date.year, - end_date.strftime('%m'), - end_date.strftime('%d'))) + row_five.append( + "{0}-{1}-{2} to {3}-{4}-{5}".format( + start_date.year, + start_date.strftime("%m"), + start_date.strftime("%d"), + end_date.year, + end_date.strftime("%m"), + end_date.strftime("%d"), + ) + ) # A6 - row_six.append('Date run') + row_six.append("Date run") # A7 - row_seven.append('{0}-{1}-{2}'.format(timezone.now().year, timezone.now().strftime('%m'), - timezone.now().strftime('%d'))) + row_seven.append( + "{0}-{1}-{2}".format( + timezone.now().year, + timezone.now().strftime("%m"), + timezone.now().strftime("%d"), + ) + ) return row_one, row_two, row_three, row_four, row_five, row_six, row_seven -def jr_one_goa_no_date(request, output_format, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): +def jr_one_goa_no_date( + request, + output_format, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): start_date = timezone.now() - relativedelta(years=2) - return jr_one_goa_no_end_date(request, output_format, start_date.year, start_date.month, start_date.day, compat, - report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_one_goa_no_end_date(request, output_format, start_year, start_month, start_day, compat=False, - report_request_id='', requestor_id='', requestor_email='', requestor_name='', - customer_ID='', customer_name=''): - return jr_one_goa_all_dates(request, output_format, start_year, start_month, start_day, timezone.now().year, - timezone.now().month, timezone.now().day, compat, report_request_id, requestor_id, - requestor_email, requestor_name, customer_ID, customer_name) - - -def jr_one_goa_all_dates(request, output_format, start_year, start_month, start_day, end_year, end_month, end_day, - compat=False, report_request_id='', requestor_id='', requestor_email='', requestor_name='', - customer_ID='', customer_name=''): - return jr_one_goa(request, output_format, - timezone.datetime(int(start_year), int(start_month), int(start_day), - tzinfo=timezone.get_current_timezone()), - timezone.datetime(int(end_year), int(end_month), int(end_day), - tzinfo=timezone.get_current_timezone()), - compat, report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_one_goa(request, output_format, start_date, end_date, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): + return jr_one_goa_no_end_date( + request, + output_format, + start_date.year, + start_date.month, + start_date.day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one_goa_no_end_date( + request, + output_format, + start_year, + start_month, + start_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_one_goa_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + timezone.now().year, + timezone.now().month, + timezone.now().day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one_goa_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + end_year, + end_month, + end_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_one_goa( + request, + output_format, + timezone.datetime( + int(start_year), + int(start_month), + int(start_day), + tzinfo=timezone.get_current_timezone(), + ), + timezone.datetime( + int(end_year), + int(end_month), + int(end_day), + tzinfo=timezone.get_current_timezone(), + ), + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_one_goa( + request, + output_format, + start_date, + end_date, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): """ Produces COUNTER JR1 GOA report in either TSV or XML formats :param request: the request object @@ -384,70 +641,169 @@ def jr_one_goa(request, output_format, start_date, end_date, compat=False, repor press_object = press.models.Press.get_press(request) # fetch the metrics - press_total, press_views, press_downloads, press_months, journals = logic.get_press_totals(start_date, - end_date, - report_months, - compat=compat) + press_total, press_views, press_downloads, press_months, journals = ( + logic.get_press_totals(start_date, end_date, report_months, compat=compat) + ) - if output_format.upper() == 'TSV': + if output_format.upper() == "TSV": # output a TSV report - return j1_tsv(start_date, end_date, journals, press_downloads, press_months, press_total, press_views, - report_months, request, press_object, 'Journal Report 1 GOA (R4)', - 'Number of Successful Gold Open Access Full-Text Article Requests by Month and Journal') + return j1_tsv( + start_date, + end_date, + journals, + press_downloads, + press_months, + press_total, + press_views, + report_months, + request, + press_object, + "Journal Report 1 GOA (R4)", + "Number of Successful Gold Open Access Full-Text Article Requests by Month and Journal", + ) else: # output an XML report - template = 'metrics/counter_jr1_goa.xml' + template = "metrics/counter_jr1_goa.xml" # SUSHI COUNTER spec at: http://www.niso.org/apps/group_public/download.php/14101/COUNTER4_1.png # unknown variables: # vendor_id - context = {'start_date': start_date.strftime('%Y-%m-%d'), - 'end_date': end_date.strftime('%Y-%m-%d'), - 'report_items': journals, - 'report_created': timezone.now(), - 'vendor_name': press_object.name, - 'item_publisher': press_object.name, - 'vendor_email': press_object.main_contact, - 'report_ID': report_request_id, - 'requestor_ID': requestor_id, - 'requestor_name': requestor_name, - 'requestor_email': requestor_email, - 'customer_ID': customer_ID, - 'customer_name': customer_name} - - return render(request, template, context, content_type='text/xml') - - -def jr_two_no_date(request, output_format, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): + context = { + "start_date": start_date.strftime("%Y-%m-%d"), + "end_date": end_date.strftime("%Y-%m-%d"), + "report_items": journals, + "report_created": timezone.now(), + "vendor_name": press_object.name, + "item_publisher": press_object.name, + "vendor_email": press_object.main_contact, + "report_ID": report_request_id, + "requestor_ID": requestor_id, + "requestor_name": requestor_name, + "requestor_email": requestor_email, + "customer_ID": customer_ID, + "customer_name": customer_name, + } + + return render(request, template, context, content_type="text/xml") + + +def jr_two_no_date( + request, + output_format, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): start_date = timezone.now() - relativedelta(years=2) - return jr_two_no_end_date(request, output_format, start_date.year, start_date.month, start_date.day, compat, - report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_two_no_end_date(request, output_format, start_year, start_month, start_day, compat=False, report_request_id='', - requestor_id='', requestor_email='', requestor_name='', customer_ID='', customer_name=''): - return jr_two_all_dates(request, output_format, start_year, start_month, start_day, timezone.now().year, - timezone.now().month, timezone.now().day, compat, report_request_id, requestor_id, - requestor_email, requestor_name, customer_ID, customer_name) - - -def jr_two_all_dates(request, output_format, start_year, start_month, start_day, end_year, end_month, end_day, - compat=False, report_request_id='', requestor_id='', requestor_email='', requestor_name='', - customer_ID='', customer_name=''): - return jr_two(request, output_format, - timezone.datetime(int(start_year), int(start_month), int(start_day), - tzinfo=timezone.get_current_timezone()), - timezone.datetime(int(end_year), int(end_month), int(end_day), - tzinfo=timezone.get_current_timezone()), - compat, report_request_id, requestor_id, requestor_email, requestor_name, customer_ID, - customer_name) - - -def jr_two(request, output_format, start_date, end_date, compat=False, report_request_id='', requestor_id='', - requestor_email='', requestor_name='', customer_ID='', customer_name=''): + return jr_two_no_end_date( + request, + output_format, + start_date.year, + start_date.month, + start_date.day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_two_no_end_date( + request, + output_format, + start_year, + start_month, + start_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_two_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + timezone.now().year, + timezone.now().month, + timezone.now().day, + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_two_all_dates( + request, + output_format, + start_year, + start_month, + start_day, + end_year, + end_month, + end_day, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): + return jr_two( + request, + output_format, + timezone.datetime( + int(start_year), + int(start_month), + int(start_day), + tzinfo=timezone.get_current_timezone(), + ), + timezone.datetime( + int(end_year), + int(end_month), + int(end_day), + tzinfo=timezone.get_current_timezone(), + ), + compat, + report_request_id, + requestor_id, + requestor_email, + requestor_name, + customer_ID, + customer_name, + ) + + +def jr_two( + request, + output_format, + start_date, + end_date, + compat=False, + report_request_id="", + requestor_id="", + requestor_email="", + requestor_name="", + customer_ID="", + customer_name="", +): """ Produces COUNTER JR1 GOA report in either TSV or XML formats :param request: the request object @@ -461,102 +817,136 @@ def jr_two(request, output_format, start_date, end_date, compat=False, report_re press_object = press.models.Press.get_press(request) # fetch the metrics - press_total, press_views, press_downloads, press_months, journals = logic.get_press_totals(start_date, - end_date, - report_months, - compat=compat) + press_total, press_views, press_downloads, press_months, journals = ( + logic.get_press_totals(start_date, end_date, report_months, compat=compat) + ) - if output_format.upper() == 'TSV': + if output_format.upper() == "TSV": # output a TSV report - return j2_tsv(start_date, end_date, journals, report_months, request, press_object, 'Journal Report 2 (R4)', - 'Access Denied to Full-text Articles by Month, Journal and Category') + return j2_tsv( + start_date, + end_date, + journals, + report_months, + request, + press_object, + "Journal Report 2 (R4)", + "Access Denied to Full-text Articles by Month, Journal and Category", + ) else: # output an XML report - template = 'metrics/counter_jr2.xml' + template = "metrics/counter_jr2.xml" # SUSHI COUNTER spec at: http://www.niso.org/apps/group_public/download.php/14101/COUNTER4_1.png # unknown variables: # vendor_id - context = {'start_date': start_date.strftime('%Y-%m-%d'), - 'end_date': end_date.strftime('%Y-%m-%d'), - 'report_items': journals, - 'report_created': timezone.now(), - 'vendor_name': press_object.name, - 'item_publisher': press_object.name, - 'vendor_email': press_object.main_contact, - 'report_ID': report_request_id, - 'requestor_ID': requestor_id, - 'requestor_name': requestor_name, - 'requestor_email': requestor_email, - 'customer_ID': customer_ID, - 'customer_name': customer_name} - - return render(request, template, context, content_type='text/xml') - - -def j2_tsv(start_date, end_date, journals, report_months, request, press_object, report_title, report_description): - - row_one, row_two, row_three, row_four, row_five, row_six, row_seven = create_tsv_header(request, - start_date, - end_date, - report_title, - report_description, - press_object) + context = { + "start_date": start_date.strftime("%Y-%m-%d"), + "end_date": end_date.strftime("%Y-%m-%d"), + "report_items": journals, + "report_created": timezone.now(), + "vendor_name": press_object.name, + "item_publisher": press_object.name, + "vendor_email": press_object.main_contact, + "report_ID": report_request_id, + "requestor_ID": requestor_id, + "requestor_name": requestor_name, + "requestor_email": requestor_email, + "customer_ID": customer_ID, + "customer_name": customer_name, + } + + return render(request, template, context, content_type="text/xml") + + +def j2_tsv( + start_date, + end_date, + journals, + report_months, + request, + press_object, + report_title, + report_description, +): + row_one, row_two, row_three, row_four, row_five, row_six, row_seven = ( + create_tsv_header( + request, + start_date, + end_date, + report_title, + report_description, + press_object, + ) + ) row_eight = [] row_nine = [] row_ten = [] - rows = [row_one, row_two, row_three, row_four, row_five, row_six, row_seven, row_eight, row_nine, row_ten] + rows = [ + row_one, + row_two, + row_three, + row_four, + row_five, + row_six, + row_seven, + row_eight, + row_nine, + row_ten, + ] # A8 - row_eight.append('Journal') + row_eight.append("Journal") # B8 - row_eight.append('Publisher') + row_eight.append("Publisher") # C8 - row_eight.append('Platform') + row_eight.append("Platform") # D8 - row_eight.append('Journal DOI') + row_eight.append("Journal DOI") # E8 - row_eight.append('Proprietary Identifier') + row_eight.append("Proprietary Identifier") # F8 - row_eight.append('Print ISSN') + row_eight.append("Print ISSN") # G8 - row_eight.append('Online ISSN') + row_eight.append("Online ISSN") # H8 # The game, not the player. - row_eight.append('Access Denied Category') + row_eight.append("Access Denied Category") # I8 - row_eight.append('Reporting Period Total') + row_eight.append("Reporting Period Total") # J8 -> [X]8 for date in report_months: - row_eight.append('{0}-{1}'.format(date.strftime('%b'), date.year)) + row_eight.append("{0}-{1}".format(date.strftime("%b"), date.year)) # A9 - row_nine.append('Total for all Journals') + row_nine.append("Total for all Journals") # B9 row_nine.append(press_object.name) # C9 - row_nine.append('Janeway') + row_nine.append("Janeway") # D9 -> G9 for x in range(1, 5): - row_nine.append('') + row_nine.append("") # H9 - row_nine.append('Access denied: concurrent/simultaneous user license limit exceeded') + row_nine.append( + "Access denied: concurrent/simultaneous user license limit exceeded" + ) # I9 row_nine.append(0) @@ -566,20 +956,20 @@ def j2_tsv(start_date, end_date, journals, report_months, request, press_object, row_nine.append(0) # A10 - row_ten.append('Total for all Journals') + row_ten.append("Total for all Journals") # B10 row_ten.append(press_object.name) # C1- - row_ten.append('Janeway') + row_ten.append("Janeway") # D10 -> G10 for x in range(1, 5): - row_ten.append('') + row_ten.append("") # H10 - row_ten.append('Access denied: content item not licensed') + row_ten.append("Access denied: content item not licensed") # I10 row_ten.append(0) @@ -589,17 +979,35 @@ def j2_tsv(start_date, end_date, journals, report_months, request, press_object, row_ten.append(0) for journal_dict in journals: - journal = journal_dict['journal'] - journal_row = [journal.name, press_object.name, 'Janeway', '', '', '', journal.issn, - 'Access denied: concurrent/simultaneous user license limit exceeded', 0] + journal = journal_dict["journal"] + journal_row = [ + journal.name, + press_object.name, + "Janeway", + "", + "", + "", + journal.issn, + "Access denied: concurrent/simultaneous user license limit exceeded", + 0, + ] for date in report_months: journal_row.append(0) rows.append(journal_row) - journal_row_two = [journal.name, press_object.name, 'Janeway', '', '', '', journal.issn, - 'Access denied: content item not licensed', 0] + journal_row_two = [ + journal.name, + press_object.name, + "Janeway", + "", + "", + "", + journal.issn, + "Access denied: content item not licensed", + 0, + ] for date in report_months: journal_row_two.append(0) @@ -607,11 +1015,11 @@ def j2_tsv(start_date, end_date, journals, report_months, request, press_object, rows.append(journal_row_two) # Create the HttpResponse object with the appropriate TSV header. - response = HttpResponse(content_type='text/tsv') - response['Content-Disposition'] = 'attachment; filename="JR2.tsv"' + response = HttpResponse(content_type="text/tsv") + response["Content-Disposition"] = 'attachment; filename="JR2.tsv"' # output the TSV - writer = csv.writer(response, delimiter='\t') + writer = csv.writer(response, delimiter="\t") for row in rows: writer.writerow(row) @@ -619,65 +1027,90 @@ def j2_tsv(start_date, end_date, journals, report_months, request, press_object, return response -def j5_tsv(start_date, end_date, journals, yops, request, press_object, report_title, report_description): - - row_one, row_two, row_three, row_four, row_five, row_six, row_seven = create_tsv_header(request, - start_date, - end_date, - report_title, - report_description, - press_object) +def j5_tsv( + start_date, + end_date, + journals, + yops, + request, + press_object, + report_title, + report_description, +): + row_one, row_two, row_three, row_four, row_five, row_six, row_seven = ( + create_tsv_header( + request, + start_date, + end_date, + report_title, + report_description, + press_object, + ) + ) row_eight = [] row_nine = [] row_ten = [] - rows = [row_one, row_two, row_three, row_four, row_five, row_six, row_seven, row_eight, row_nine, row_ten] + rows = [ + row_one, + row_two, + row_three, + row_four, + row_five, + row_six, + row_seven, + row_eight, + row_nine, + row_ten, + ] # A8 - row_eight.append('Journal') + row_eight.append("Journal") # B8 - row_eight.append('Publisher') + row_eight.append("Publisher") # C8 - row_eight.append('Platform') + row_eight.append("Platform") # D8 - row_eight.append('Journal DOI') + row_eight.append("Journal DOI") # E8 - row_eight.append('Proprietary Identifier') + row_eight.append("Proprietary Identifier") # F8 - row_eight.append('Print ISSN') + row_eight.append("Print ISSN") # G8 - row_eight.append('Online ISSN') + row_eight.append("Online ISSN") # H8 # The game, not the player. - row_eight.append('Articles in Press') + row_eight.append("Articles in Press") # I8 -> [X]8 for year in yops: - row_eight.append('YOP {0}'.format(yops[year])) + row_eight.append("YOP {0}".format(yops[year])) # A9 - row_nine.append('Total for all Journals') + row_nine.append("Total for all Journals") # B9 row_nine.append(press_object.name) # C9 - row_nine.append('Janeway') + row_nine.append("Janeway") # D9 -> G9 for x in range(1, 5): - row_nine.append('') + row_nine.append("") # H9 - row_nine.append('Access denied: concurrent/simultaneous user license limit exceeded') + row_nine.append( + "Access denied: concurrent/simultaneous user license limit exceeded" + ) # I9 row_nine.append(0) @@ -687,20 +1120,20 @@ def j5_tsv(start_date, end_date, journals, yops, request, press_object, report_t row_nine.append(0) # A10 - row_ten.append('Total for all Journals') + row_ten.append("Total for all Journals") # B10 row_ten.append(press_object.name) # C1- - row_ten.append('Janeway') + row_ten.append("Janeway") # D10 -> G10 for x in range(1, 5): - row_ten.append('') + row_ten.append("") # H10 - row_ten.append('Access denied: content item not licensed') + row_ten.append("Access denied: content item not licensed") # I10 row_ten.append(0) @@ -710,17 +1143,35 @@ def j5_tsv(start_date, end_date, journals, yops, request, press_object, report_t row_ten.append(0) for journal_dict in journals: - journal = journal_dict['journal'] - journal_row = [journal.name, press_object.name, 'Janeway', '', '', '', journal.issn, - 'Access denied: concurrent/simultaneous user license limit exceeded', 0] + journal = journal_dict["journal"] + journal_row = [ + journal.name, + press_object.name, + "Janeway", + "", + "", + "", + journal.issn, + "Access denied: concurrent/simultaneous user license limit exceeded", + 0, + ] for date in report_months: journal_row.append(0) rows.append(journal_row) - journal_row_two = [journal.name, press_object.name, 'Janeway', '', '', '', journal.issn, - 'Access denied: content item not licensed', 0] + journal_row_two = [ + journal.name, + press_object.name, + "Janeway", + "", + "", + "", + journal.issn, + "Access denied: content item not licensed", + 0, + ] for date in report_months: journal_row_two.append(0) @@ -728,11 +1179,11 @@ def j5_tsv(start_date, end_date, journals, yops, request, press_object, report_t rows.append(journal_row_two) # Create the HttpResponse object with the appropriate TSV header. - response = HttpResponse(content_type='text/tsv') - response['Content-Disposition'] = 'attachment; filename="JR2.tsv"' + response = HttpResponse(content_type="text/tsv") + response["Content-Disposition"] = 'attachment; filename="JR2.tsv"' # output the TSV - writer = csv.writer(response, delimiter='\t') + writer = csv.writer(response, delimiter="\t") for row in rows: writer.writerow(row) diff --git a/src/preprint/apps.py b/src/preprint/apps.py index ce21736022..eccf06fe03 100755 --- a/src/preprint/apps.py +++ b/src/preprint/apps.py @@ -7,7 +7,8 @@ class PreprintConfig(AppConfig): - name = 'preprint' + name = "preprint" + # THE PREPRINTS APP HAS BEEN RENAMED REPOSITORY AND THSI WILL BE REMOVED IN # 1.3.9 diff --git a/src/preprint/migrations/0001_initial.py b/src/preprint/migrations/0001_initial.py index b59aeb1ec6..a7c21635d3 100755 --- a/src/preprint/migrations/0001_initial.py +++ b/src/preprint/migrations/0001_initial.py @@ -7,43 +7,88 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('core', '0001_initial'), - ('submission', '0001_initial'), + ("core", "0001_initial"), + ("submission", "0001_initial"), ] operations = [ migrations.CreateModel( - name='DOIPurchase', + name="DOIPurchase", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('transaction', models.CharField(max_length=100)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("transaction", models.CharField(max_length=100)), ], ), migrations.CreateModel( - name='Preprint', + name="Preprint", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('doi', models.CharField(max_length=100)), - ('curent_version', models.IntegerField(default=1)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("doi", models.CharField(max_length=100)), + ("curent_version", models.IntegerField(default=1)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], ), migrations.CreateModel( - name='PreprintVersion', + name="PreprintVersion", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('version', models.IntegerField(default=1)), - ('manuscript_files', models.ManyToManyField(blank=True, null=True, related_name='version_files', to='core.File')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='preprint.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("version", models.IntegerField(default=1)), + ( + "manuscript_files", + models.ManyToManyField( + blank=True, + null=True, + related_name="version_files", + to="core.File", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="preprint.Preprint", + ), + ), ], ), migrations.AddField( - model_name='doipurchase', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='preprint.Preprint'), + model_name="doipurchase", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="preprint.Preprint" + ), ), ] diff --git a/src/preprint/migrations/0002_auto_20171009_1134.py b/src/preprint/migrations/0002_auto_20171009_1134.py index b9af8ae35a..024cb0c480 100755 --- a/src/preprint/migrations/0002_auto_20171009_1134.py +++ b/src/preprint/migrations/0002_auto_20171009_1134.py @@ -8,31 +8,56 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0014_auto_20171009_1028'), + ("submission", "0014_auto_20171009_1028"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('preprint', '0001_initial'), + ("preprint", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Comment', + name="Comment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('body', models.TextField(verbose_name='Comment')), - ('is_reviewed', models.BooleanField(default=False)), - ('is_public', models.BooleanField(default=False)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('reply_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='preprint.Comment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("body", models.TextField(verbose_name="Comment")), + ("is_reviewed", models.BooleanField(default=False)), + ("is_public", models.BooleanField(default=False)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "reply_to", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="preprint.Comment", + ), + ), ], ), migrations.RemoveField( - model_name='doipurchase', - name='preprint', + model_name="doipurchase", + name="preprint", ), migrations.DeleteModel( - name='DOIPurchase', + name="DOIPurchase", ), ] diff --git a/src/preprint/migrations/0003_auto_20171009_1206.py b/src/preprint/migrations/0003_auto_20171009_1206.py index 682efbf20f..c7eda7a419 100755 --- a/src/preprint/migrations/0003_auto_20171009_1206.py +++ b/src/preprint/migrations/0003_auto_20171009_1206.py @@ -7,20 +7,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('preprint', '0002_auto_20171009_1134'), + ("preprint", "0002_auto_20171009_1134"), ] operations = [ migrations.AlterField( - model_name='comment', - name='body', - field=models.TextField(verbose_name='Write your comment:'), + model_name="comment", + name="body", + field=models.TextField(verbose_name="Write your comment:"), ), migrations.AlterField( - model_name='comment', - name='reply_to', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='preprint.Comment'), + model_name="comment", + name="reply_to", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="preprint.Comment", + ), ), ] diff --git a/src/preprint/migrations/0004_comment_date_time.py b/src/preprint/migrations/0004_comment_date_time.py index 3ecebad7a0..aba9edc688 100755 --- a/src/preprint/migrations/0004_comment_date_time.py +++ b/src/preprint/migrations/0004_comment_date_time.py @@ -7,15 +7,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('preprint', '0003_auto_20171009_1206'), + ("preprint", "0003_auto_20171009_1206"), ] operations = [ migrations.AddField( - model_name='comment', - name='date_time', + model_name="comment", + name="date_time", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/src/preprint/migrations/0005_auto_20171010_1132.py b/src/preprint/migrations/0005_auto_20171010_1132.py index 6838f5d9f6..77c33a70c3 100755 --- a/src/preprint/migrations/0005_auto_20171010_1132.py +++ b/src/preprint/migrations/0005_auto_20171010_1132.py @@ -7,35 +7,43 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0010_account_enable_public_profile'), - ('preprint', '0004_comment_date_time'), + ("core", "0010_account_enable_public_profile"), + ("preprint", "0004_comment_date_time"), ] operations = [ migrations.AlterModelOptions( - name='comment', - options={'ordering': ('-date_time', '-pk')}, + name="comment", + options={"ordering": ("-date_time", "-pk")}, ), migrations.RemoveField( - model_name='preprintversion', - name='manuscript_files', + model_name="preprintversion", + name="manuscript_files", ), migrations.AddField( - model_name='preprintversion', - name='galley', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.Galley'), + model_name="preprintversion", + name="galley", + field=models.ForeignKey( + default=1, on_delete=django.db.models.deletion.CASCADE, to="core.Galley" + ), preserve_default=False, ), migrations.AlterField( - model_name='comment', - name='reply_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='preprint.Comment'), + model_name="comment", + name="reply_to", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="preprint.Comment", + ), ), migrations.AlterField( - model_name='preprintversion', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="preprintversion", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), ] diff --git a/src/preprint/migrations/0006_preprintversion_date_time.py b/src/preprint/migrations/0006_preprintversion_date_time.py index 777e8a2d99..7777855494 100755 --- a/src/preprint/migrations/0006_preprintversion_date_time.py +++ b/src/preprint/migrations/0006_preprintversion_date_time.py @@ -7,15 +7,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('preprint', '0005_auto_20171010_1132'), + ("preprint", "0005_auto_20171010_1132"), ] operations = [ migrations.AddField( - model_name='preprintversion', - name='date_time', + model_name="preprintversion", + name="date_time", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/src/preprint/migrations/0007_auto_20171013_1104.py b/src/preprint/migrations/0007_auto_20171013_1104.py index f6e670266f..ec8963517a 100755 --- a/src/preprint/migrations/0007_auto_20171013_1104.py +++ b/src/preprint/migrations/0007_auto_20171013_1104.py @@ -7,29 +7,36 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0015_auto_20171013_1104'), + ("submission", "0015_auto_20171013_1104"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('preprint', '0006_preprintversion_date_time'), + ("preprint", "0006_preprintversion_date_time"), ] operations = [ migrations.CreateModel( - name='Subject', + name="Subject", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField()), - ('editors', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), - ('preprints', models.ManyToManyField(to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField()), + ("editors", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ("preprints", models.ManyToManyField(to="submission.Article")), ], options={ - 'ordering': ('slug', 'pk'), + "ordering": ("slug", "pk"), }, ), migrations.AlterModelOptions( - name='preprintversion', - options={'ordering': ('-date_time', '-id')}, + name="preprintversion", + options={"ordering": ("-date_time", "-id")}, ), ] diff --git a/src/preprint/migrations/0008_auto_20171013_1104.py b/src/preprint/migrations/0008_auto_20171013_1104.py index f14e36a55e..555d023b6c 100755 --- a/src/preprint/migrations/0008_auto_20171013_1104.py +++ b/src/preprint/migrations/0008_auto_20171013_1104.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('preprint', '0007_auto_20171013_1104'), + ("preprint", "0007_auto_20171013_1104"), ] operations = [ migrations.AlterField( - model_name='subject', - name='slug', + model_name="subject", + name="slug", field=models.SlugField(blank=True), ), ] diff --git a/src/preprint/migrations/0009_subject_enabled.py b/src/preprint/migrations/0009_subject_enabled.py index 74a0068a75..3f66b8bd56 100755 --- a/src/preprint/migrations/0009_subject_enabled.py +++ b/src/preprint/migrations/0009_subject_enabled.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('preprint', '0008_auto_20171013_1104'), + ("preprint", "0008_auto_20171013_1104"), ] operations = [ migrations.AddField( - model_name='subject', - name='enabled', - field=models.BooleanField(default=True, help_text='If disabled, this subject will not appear publicly.'), + model_name="subject", + name="enabled", + field=models.BooleanField( + default=True, + help_text="If disabled, this subject will not appear publicly.", + ), ), ] diff --git a/src/preprint/migrations/0010_versionqueue.py b/src/preprint/migrations/0010_versionqueue.py index 4696a7f4a3..451e6e9b3f 100644 --- a/src/preprint/migrations/0010_versionqueue.py +++ b/src/preprint/migrations/0010_versionqueue.py @@ -8,24 +8,51 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), - ('core', '0011_auto_20171010_1535'), - ('preprint', '0009_subject_enabled'), + ("submission", "0019_auto_20171130_1115"), + ("core", "0011_auto_20171010_1535"), + ("preprint", "0009_subject_enabled"), ] operations = [ migrations.CreateModel( - name='VersionQueue', + name="VersionQueue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('update_type', models.CharField(choices=[('correction', 'Correction'), ('version', 'Version')], max_length=10)), - ('date_submitted', models.DateTimeField(default=django.utils.timezone.now)), - ('date_decision', models.DateTimeField(blank=True, null=True)), - ('approved', models.BooleanField(default=False)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.File')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "update_type", + models.CharField( + choices=[("correction", "Correction"), ("version", "Version")], + max_length=10, + ), + ), + ( + "date_submitted", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("date_decision", models.DateTimeField(blank=True, null=True)), + ("approved", models.BooleanField(default=False)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.File" + ), + ), ], ), ] diff --git a/src/preprint/migrations/0011_auto_20171130_1507.py b/src/preprint/migrations/0011_auto_20171130_1507.py index 3187d4bb88..08c222ca62 100644 --- a/src/preprint/migrations/0011_auto_20171130_1507.py +++ b/src/preprint/migrations/0011_auto_20171130_1507.py @@ -7,22 +7,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0011_auto_20171010_1535'), - ('preprint', '0010_versionqueue'), + ("core", "0011_auto_20171010_1535"), + ("preprint", "0010_versionqueue"), ] operations = [ migrations.AddField( - model_name='versionqueue', - name='galley', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.Galley'), + model_name="versionqueue", + name="galley", + field=models.ForeignKey( + default=1, on_delete=django.db.models.deletion.CASCADE, to="core.Galley" + ), preserve_default=False, ), migrations.AlterField( - model_name='versionqueue', - name='update_type', - field=models.CharField(choices=[('correction', 'Correction'), ('version', 'New Version')], max_length=10), + model_name="versionqueue", + name="update_type", + field=models.CharField( + choices=[("correction", "Correction"), ("version", "New Version")], + max_length=10, + ), ), ] diff --git a/src/preprint/models.py b/src/preprint/models.py index 1bb1f76b1a..1e9b5d2f26 100755 --- a/src/preprint/models.py +++ b/src/preprint/models.py @@ -7,4 +7,4 @@ # AS OF 1.3.8 PREPRINTS HAS BEEN RENAMED REPOSITORY. # THIS DIRECTORY WILL BE REMOVED IN 1.3.9 ALONG WITH ALL OF THE -# PREPRINTS TABLES. \ No newline at end of file +# PREPRINTS TABLES. diff --git a/src/press/admin.py b/src/press/admin.py index 2889907aee..421035cd31 100755 --- a/src/press/admin.py +++ b/src/press/admin.py @@ -12,30 +12,30 @@ class PressAdmin(SummernoteModelAdmin): - list_display = ('name', 'domain', 'theme', 'is_secure') + list_display = ("name", "domain", "theme", "is_secure") raw_id_fields = ( - 'thumbnail_image', - 'carousel', - 'featured_journals', - 'carousel_news_items', - 'homepage_preprints', + "thumbnail_image", + "carousel", + "featured_journals", + "carousel_news_items", + "homepage_preprints", ) class PressSettingAdmin(admin.ModelAdmin): - list_display = ('pk', 'name', 'is_boolean') - list_filter = ('is_boolean',) - search_fields = ('name', 'value') + list_display = ("pk", "name", "is_boolean") + list_filter = ("is_boolean",) + search_fields = ("name", "value") class StaffGroupAdmin(admin.ModelAdmin): list_display = ( - 'name', - 'sequence', + "name", + "sequence", ) search_fields = ( - 'name', - 'description', + "name", + "description", ) inlines = [ @@ -45,24 +45,22 @@ class StaffGroupAdmin(admin.ModelAdmin): class StaffGroupMemberAdmin(admin.ModelAdmin): list_display = ( - 'user', - 'job_title', - 'group', - 'sequence', - ) - raw_id_fields = ( - 'user', + "user", + "job_title", + "group", + "sequence", ) + raw_id_fields = ("user",) list_filter = ( - 'user__enable_public_profile', - 'group', + "user__enable_public_profile", + "group", ) search_fields = ( - 'user__email', - 'user__first_name', - 'user__last_name', - 'job_title', - 'alternate_title', + "user__email", + "user__first_name", + "user__last_name", + "job_title", + "alternate_title", ) diff --git a/src/press/apps.py b/src/press/apps.py index e8ba17eca0..ed73049854 100755 --- a/src/press/apps.py +++ b/src/press/apps.py @@ -7,4 +7,4 @@ class PressConfig(AppConfig): - name = 'press' + name = "press" diff --git a/src/press/decorators.py b/src/press/decorators.py index b0bde55401..7953e071d1 100644 --- a/src/press/decorators.py +++ b/src/press/decorators.py @@ -16,10 +16,6 @@ def disable_journals_wrapper(request, *args, **kwargs): if request.press and not request.press.disable_journals: return func(request, *args, **kwargs) - return redirect( - reverse( - 'website_index' - ) - ) + return redirect(reverse("website_index")) - return disable_journals_wrapper \ No newline at end of file + return disable_journals_wrapper diff --git a/src/press/forms.py b/src/press/forms.py index cfcbe5e5db..6835a6338f 100755 --- a/src/press/forms.py +++ b/src/press/forms.py @@ -15,7 +15,6 @@ class PressForm(forms.ModelForm): - press_logo = forms.FileField( required=False, widget=JanewayFileInput, @@ -24,46 +23,44 @@ class PressForm(forms.ModelForm): class Meta: model = models.Press fields = ( - 'name', - 'main_contact', - 'theme', - 'description', - 'footer_description', - 'journal_footer_text', - 'secondary_image', - 'secondary_image_url', - 'default_carousel_image', - 'favicon', - 'enable_preprints', - 'is_secure', - 'password_number', - 'password_upper', - 'password_length', - 'password_reset_text', - 'registration_text', - 'tracking_code', - 'disable_journals', - 'privacy_policy_url', + "name", + "main_contact", + "theme", + "description", + "footer_description", + "journal_footer_text", + "secondary_image", + "secondary_image_url", + "default_carousel_image", + "favicon", + "enable_preprints", + "is_secure", + "password_number", + "password_upper", + "password_length", + "password_reset_text", + "registration_text", + "tracking_code", + "disable_journals", + "privacy_policy_url", ) widgets = { - 'theme': forms.Select( - choices=logic.get_theme_list() - ), - 'footer_description': TinyMCE(), - 'journal_footer_text': TinyMCE(), - 'password_reset_text': TinyMCE(), - 'registration_text': TinyMCE(), - 'description': TinyMCE(), + "theme": forms.Select(choices=logic.get_theme_list()), + "footer_description": TinyMCE(), + "journal_footer_text": TinyMCE(), + "password_reset_text": TinyMCE(), + "registration_text": TinyMCE(), + "description": TinyMCE(), } def save(self, commit=True): press = super(PressForm, self).save(commit=False) request = GlobalRequestMiddleware.get_current_request() - file = self.cleaned_data.get('press_logo', None) + file = self.cleaned_data.get("press_logo", None) if file: - file = files.save_file_to_press(request, file, 'Press Logo', '') + file = files.save_file_to_press(request, file, "Press Logo", "") # Delete the old file from the disk if press.thumbnail_image: @@ -78,35 +75,36 @@ def save(self, commit=True): class PressJournalDescription(forms.Form): - description = forms.CharField(widget=TinyMCE) def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal') + self.journal = kwargs.pop("journal") super(PressJournalDescription, self).__init__(*args, **kwargs) - self.fields['description'].initial = self.journal.get_setting( - group_name='general', - setting_name='press_journal_description', + self.fields["description"].initial = self.journal.get_setting( + group_name="general", + setting_name="press_journal_description", ) def save(self, commit=True): - description = self.cleaned_data.get('description') + description = self.cleaned_data.get("description") if commit: setting_handler.save_setting( - 'general', - 'press_journal_description', + "general", + "press_journal_description", self.journal, description, ) + class StaffGroupMemberForm(forms.ModelForm): """Lets a staff member edit a few fields related to their press staff profile """ + class Meta: model = models.StaffGroupMember - exclude = ('group', 'user', 'sequence') + exclude = ("group", "user", "sequence") widgets = { - 'publications': TinyMCE(), + "publications": TinyMCE(), } diff --git a/src/press/logic.py b/src/press/logic.py index 465e89294e..91f9efa665 100755 --- a/src/press/logic.py +++ b/src/press/logic.py @@ -8,13 +8,14 @@ def get_carousel_items(request): - if request.press.carousel_type == 'articles': + if request.press.carousel_type == "articles": carousel_objects = submission_models.Article.objects.all().order_by( - "-date_published")[:request.press.carousel_items] + "-date_published" + )[: request.press.carousel_items] return carousel_objects - if request.press.carousel_type == 'news': + if request.press.carousel_type == "news": news_objects = request.press.carousel_news_items.all() return news_objects diff --git a/src/press/migrations/0001_initial.py b/src/press/migrations/0001_initial.py index dc77c33cbc..0b3c9bad63 100755 --- a/src/press/migrations/0001_initial.py +++ b/src/press/migrations/0001_initial.py @@ -9,38 +9,112 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('journal', '0001_initial'), - ('core', '0001_initial'), - ('carousel', '0001_initial'), + ("journal", "0001_initial"), + ("core", "0001_initial"), + ("carousel", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Press', + name="Press", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=600)), - ('domain', models.CharField(default='localhost', max_length=255, unique=True)), - ('footer_description', models.TextField(blank=True, null=True)), - ('main_contact', models.EmailField(default='janeway@voyager.com', max_length=254)), - ('theme', models.CharField(default='press', max_length=255)), - ('homepage_news_items', models.PositiveIntegerField(default=5)), - ('carousel_type', models.CharField(choices=[('articles', 'Latest Articles'), ('news', 'Latest News'), ('news_and_articles', 'Latest News and Articles')], default='articles', max_length=30)), - ('carousel_items', models.PositiveIntegerField(default=4)), - ('default_carousel_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=press.models.cover_images_upload_path)), - ('favicon', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=press.models.cover_images_upload_path)), - ('is_secure', models.BooleanField(default=False, help_text='If the press should redirect to HTTPS, mark this.')), - ('random_featured_journals', models.BooleanField(default=False)), - ('password_reset_text', models.TextField(blank=True, null=True)), - ('registration_text', models.TextField(blank=True, null=True)), - ('carousel', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='press', to='carousel.Carousel')), - ('carousel_news_items', models.ManyToManyField(blank=True, null=True, to='core.NewsItem')), - ('featured_journals', models.ManyToManyField(blank=True, null=True, to='journal.Journal')), - ('thumbnail_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='press_thumbnail_image', to='core.File')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=600)), + ( + "domain", + models.CharField(default="localhost", max_length=255, unique=True), + ), + ("footer_description", models.TextField(blank=True, null=True)), + ( + "main_contact", + models.EmailField(default="janeway@voyager.com", max_length=254), + ), + ("theme", models.CharField(default="press", max_length=255)), + ("homepage_news_items", models.PositiveIntegerField(default=5)), + ( + "carousel_type", + models.CharField( + choices=[ + ("articles", "Latest Articles"), + ("news", "Latest News"), + ("news_and_articles", "Latest News and Articles"), + ], + default="articles", + max_length=30, + ), + ), + ("carousel_items", models.PositiveIntegerField(default=4)), + ( + "default_carousel_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), + ), + ( + "favicon", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), + ), + ( + "is_secure", + models.BooleanField( + default=False, + help_text="If the press should redirect to HTTPS, mark this.", + ), + ), + ("random_featured_journals", models.BooleanField(default=False)), + ("password_reset_text", models.TextField(blank=True, null=True)), + ("registration_text", models.TextField(blank=True, null=True)), + ( + "carousel", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="press", + to="carousel.Carousel", + ), + ), + ( + "carousel_news_items", + models.ManyToManyField(blank=True, null=True, to="core.NewsItem"), + ), + ( + "featured_journals", + models.ManyToManyField(blank=True, null=True, to="journal.Journal"), + ), + ( + "thumbnail_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="press_thumbnail_image", + to="core.File", + ), + ), ], ), ] diff --git a/src/press/migrations/0002_auto_20170727_1504.py b/src/press/migrations/0002_auto_20170727_1504.py index c7fceaf15d..3ec98c377c 100755 --- a/src/press/migrations/0002_auto_20170727_1504.py +++ b/src/press/migrations/0002_auto_20170727_1504.py @@ -7,25 +7,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0001_initial'), + ("press", "0001_initial"), ] operations = [ migrations.AddField( - model_name='press', - name='password_length', - field=models.PositiveIntegerField(default=12, validators=[django.core.validators.MinValueValidator(9)]), + model_name="press", + name="password_length", + field=models.PositiveIntegerField( + default=12, validators=[django.core.validators.MinValueValidator(9)] + ), ), migrations.AddField( - model_name='press', - name='password_number', - field=models.BooleanField(default=False, help_text='If set, passwords must include one number.'), + model_name="press", + name="password_number", + field=models.BooleanField( + default=False, help_text="If set, passwords must include one number." + ), ), migrations.AddField( - model_name='press', - name='password_upper', - field=models.BooleanField(default=False, help_text='If set, passwords must include one upper case.'), + model_name="press", + name="password_upper", + field=models.BooleanField( + default=False, + help_text="If set, passwords must include one upper case.", + ), ), ] diff --git a/src/press/migrations/0002_auto_20170813_1302.py b/src/press/migrations/0002_auto_20170813_1302.py index 7a59dca878..ddb0c226e9 100755 --- a/src/press/migrations/0002_auto_20170813_1302.py +++ b/src/press/migrations/0002_auto_20170813_1302.py @@ -8,20 +8,33 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0001_initial'), + ("press", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='press', - name='default_carousel_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="default_carousel_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='press', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), ), ] diff --git a/src/press/migrations/0003_auto_20170829_1501.py b/src/press/migrations/0003_auto_20170829_1501.py index 1fd926af09..2b169a5da8 100755 --- a/src/press/migrations/0003_auto_20170829_1501.py +++ b/src/press/migrations/0003_auto_20170829_1501.py @@ -6,16 +6,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0002_auto_20170813_1302'), - ('comms', '0001_initial'), + ("press", "0002_auto_20170813_1302"), + ("comms", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='press', - name='carousel_news_items', - field=models.ManyToManyField(blank=True, null=True, to='comms.NewsItem'), + model_name="press", + name="carousel_news_items", + field=models.ManyToManyField(blank=True, null=True, to="comms.NewsItem"), ), ] diff --git a/src/press/migrations/0004_merge_20170908_1215.py b/src/press/migrations/0004_merge_20170908_1215.py index 22fc031fad..59987c59eb 100755 --- a/src/press/migrations/0004_merge_20170908_1215.py +++ b/src/press/migrations/0004_merge_20170908_1215.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0002_auto_20170727_1504'), - ('press', '0003_auto_20170829_1501'), + ("press", "0002_auto_20170727_1504"), + ("press", "0003_auto_20170829_1501"), ] - operations = [ - ] + operations = [] diff --git a/src/press/migrations/0005_auto_20170929_0834.py b/src/press/migrations/0005_auto_20170929_0834.py index b5705741fc..5e6af44a7a 100755 --- a/src/press/migrations/0005_auto_20170929_0834.py +++ b/src/press/migrations/0005_auto_20170929_0834.py @@ -8,35 +8,56 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0004_merge_20170908_1215'), + ("press", "0004_merge_20170908_1215"), ] operations = [ migrations.AddField( - model_name='press', - name='enable_preprints', + model_name="press", + name="enable_preprints", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='press', - name='default_carousel_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="default_carousel_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='press', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=press.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='press', - name='password_reset_text', - field=models.TextField(blank=True, default='

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{{ request.press_base_url }}{% url \'core_reset_password\' reset_token.token %}

{{request.press.name}}

', null=True), + model_name="press", + name="password_reset_text", + field=models.TextField( + blank=True, + default="

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{{ request.press_base_url }}{% url 'core_reset_password' reset_token.token %}

{{request.press.name}}

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='registration_text', - field=models.TextField(blank=True, default='

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{{ request.press_base_url }}{% url \'core_confirm_account\' user.confirmation_code %}
{{request.press.name}}

', null=True), + model_name="press", + name="registration_text", + field=models.TextField( + blank=True, + default="

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{{ request.press_base_url }}{% url 'core_confirm_account' user.confirmation_code %}
{{request.press.name}}

", + null=True, + ), ), ] diff --git a/src/press/migrations/0006_press_preprints_about.py b/src/press/migrations/0006_press_preprints_about.py index 4b37727bdb..096cfac739 100755 --- a/src/press/migrations/0006_press_preprints_about.py +++ b/src/press/migrations/0006_press_preprints_about.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0005_auto_20170929_0834'), + ("press", "0005_auto_20170929_0834"), ] operations = [ migrations.AddField( - model_name='press', - name='preprints_about', + model_name="press", + name="preprints_about", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/press/migrations/0007_auto_20171004_1605.py b/src/press/migrations/0007_auto_20171004_1605.py index 3469b8315d..41ec3242d4 100755 --- a/src/press/migrations/0007_auto_20171004_1605.py +++ b/src/press/migrations/0007_auto_20171004_1605.py @@ -6,20 +6,22 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0006_press_preprints_about'), + ("press", "0006_press_preprints_about"), ] operations = [ migrations.AddField( - model_name='press', - name='preprint_pdf_only', - field=models.BooleanField(default=True, help_text='Forces manuscript files to be PDFs for Preprints.'), + model_name="press", + name="preprint_pdf_only", + field=models.BooleanField( + default=True, + help_text="Forces manuscript files to be PDFs for Preprints.", + ), ), migrations.AddField( - model_name='press', - name='preprint_start', + model_name="press", + name="preprint_start", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/press/migrations/0008_auto_20171005_1158.py b/src/press/migrations/0008_auto_20171005_1158.py index d6cbb91365..1252e80ee9 100755 --- a/src/press/migrations/0008_auto_20171005_1158.py +++ b/src/press/migrations/0008_auto_20171005_1158.py @@ -6,20 +6,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0007_auto_20171004_1605'), + ("press", "0007_auto_20171004_1605"), ] operations = [ migrations.AddField( - model_name='press', - name='preprint_publication', - field=models.TextField(blank=True, default='

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_publication", + field=models.TextField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), migrations.AddField( - model_name='press', - name='preprint_submission', - field=models.TextField(blank=True, default='

Dear {{ article.owner.full_name }},

Thank you for your preprint submission. Our Preprint Editor has been notified and will review it before making a decision.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_submission", + field=models.TextField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Thank you for your preprint submission. Our Preprint Editor has been notified and will review it before making a decision.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), ] diff --git a/src/press/migrations/0009_press_preprint_editors.py b/src/press/migrations/0009_press_preprint_editors.py index a260ee96f8..ffb896c98c 100755 --- a/src/press/migrations/0009_press_preprint_editors.py +++ b/src/press/migrations/0009_press_preprint_editors.py @@ -7,16 +7,15 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('press', '0008_auto_20171005_1158'), + ("press", "0008_auto_20171005_1158"), ] operations = [ migrations.AddField( - model_name='press', - name='preprint_editors', + model_name="press", + name="preprint_editors", field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), ), ] diff --git a/src/press/migrations/0010_presssetting.py b/src/press/migrations/0010_presssetting.py index 608a196880..97b8cb97fc 100755 --- a/src/press/migrations/0010_presssetting.py +++ b/src/press/migrations/0010_presssetting.py @@ -7,19 +7,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0009_press_preprint_editors'), + ("press", "0009_press_preprint_editors"), ] operations = [ migrations.CreateModel( - name='PressSetting', + name="PressSetting", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('value', models.TextField(blank=True, null=True)), - ('press', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='press.Press')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("value", models.TextField(blank=True, null=True)), + ( + "press", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="press.Press" + ), + ), ], ), ] diff --git a/src/press/migrations/0011_remove_press_preprint_editors.py b/src/press/migrations/0011_remove_press_preprint_editors.py index d3b3bf5a05..35201a4d60 100755 --- a/src/press/migrations/0011_remove_press_preprint_editors.py +++ b/src/press/migrations/0011_remove_press_preprint_editors.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0010_presssetting'), + ("press", "0010_presssetting"), ] operations = [ migrations.RemoveField( - model_name='press', - name='preprint_editors', + model_name="press", + name="preprint_editors", ), ] diff --git a/src/press/migrations/0012_presssetting_is_boolean.py b/src/press/migrations/0012_presssetting_is_boolean.py index 87e855e6fa..a875b168cc 100755 --- a/src/press/migrations/0012_presssetting_is_boolean.py +++ b/src/press/migrations/0012_presssetting_is_boolean.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0011_remove_press_preprint_editors'), + ("press", "0011_remove_press_preprint_editors"), ] operations = [ migrations.AddField( - model_name='presssetting', - name='is_boolean', + model_name="presssetting", + name="is_boolean", field=models.BooleanField(default=False), ), ] diff --git a/src/press/migrations/0013_press_preprint_decline.py b/src/press/migrations/0013_press_preprint_decline.py index 13179a7f1c..c2146cda9f 100644 --- a/src/press/migrations/0013_press_preprint_decline.py +++ b/src/press/migrations/0013_press_preprint_decline.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0012_presssetting_is_boolean'), + ("press", "0012_presssetting_is_boolean"), ] operations = [ migrations.AddField( - model_name='press', - name='preprint_decline', - field=models.TextField(blank=True, default='

Dear {{ article.owner.full_name }},

Unfortunately we have decided to decline to publish your preprint

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_decline", + field=models.TextField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Unfortunately we have decided to decline to publish your preprint

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), ] diff --git a/src/press/migrations/0014_auto_20171127_1502.py b/src/press/migrations/0014_auto_20171127_1502.py index 5bfaa8160a..05314a1bf5 100644 --- a/src/press/migrations/0014_auto_20171127_1502.py +++ b/src/press/migrations/0014_auto_20171127_1502.py @@ -6,26 +6,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0017_auto_20171114_1502'), - ('press', '0013_press_preprint_decline'), + ("submission", "0017_auto_20171114_1502"), + ("press", "0013_press_preprint_decline"), ] operations = [ migrations.AddField( - model_name='press', - name='homepage_preprints', - field=models.ManyToManyField(to='submission.Article', blank=True), + model_name="press", + name="homepage_preprints", + field=models.ManyToManyField(to="submission.Article", blank=True), ), migrations.AddField( - model_name='press', - name='random_homepage_preprints', + model_name="press", + name="random_homepage_preprints", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='press', - name='preprint_decline', - field=models.TextField(blank=True, default='

Dear {{ article.owner.full_name }},

Unfortunately we have decided not to publish your preprint.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_decline", + field=models.TextField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Unfortunately we have decided not to publish your preprint.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), ] diff --git a/src/press/migrations/0015_auto_20180208_1442.py b/src/press/migrations/0015_auto_20180208_1442.py index c83530de63..bf0e025f6d 100644 --- a/src/press/migrations/0015_auto_20180208_1442.py +++ b/src/press/migrations/0015_auto_20180208_1442.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0014_auto_20171127_1502'), + ("press", "0014_auto_20171127_1502"), ] operations = [ migrations.AlterField( - model_name='press', - name='theme', - field=models.CharField(default='default', max_length=255), + model_name="press", + name="theme", + field=models.CharField(default="default", max_length=255), ), ] diff --git a/src/press/migrations/0015_press_tracking_code.py b/src/press/migrations/0015_press_tracking_code.py index a095ab069e..9f32915ea7 100644 --- a/src/press/migrations/0015_press_tracking_code.py +++ b/src/press/migrations/0015_press_tracking_code.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0014_auto_20171127_1502'), + ("press", "0014_auto_20171127_1502"), ] operations = [ migrations.AddField( - model_name='press', - name='tracking_code', + model_name="press", + name="tracking_code", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/press/migrations/0016_merge_20180319_1139.py b/src/press/migrations/0016_merge_20180319_1139.py index f0a0a1cc99..d828c65189 100644 --- a/src/press/migrations/0016_merge_20180319_1139.py +++ b/src/press/migrations/0016_merge_20180319_1139.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0015_auto_20180208_1442'), - ('press', '0015_press_tracking_code'), + ("press", "0015_auto_20180208_1442"), + ("press", "0015_press_tracking_code"), ] - operations = [ - ] + operations = [] diff --git a/src/press/migrations/0017_auto_20181116_1144.py b/src/press/migrations/0017_auto_20181116_1144.py index ac08ba1127..85375e2b82 100644 --- a/src/press/migrations/0017_auto_20181116_1144.py +++ b/src/press/migrations/0017_auto_20181116_1144.py @@ -8,20 +8,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0016_merge_20180319_1139'), + ("press", "0016_merge_20180319_1139"), ] operations = [ migrations.AlterField( - model_name='press', - name='default_carousel_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="default_carousel_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=press.models.cover_images_upload_path, + ), ), migrations.AlterField( - model_name='press', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=press.models.cover_images_upload_path), + model_name="press", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=press.models.cover_images_upload_path, + ), ), ] diff --git a/src/press/migrations/0018_auto_20181123_2111.py b/src/press/migrations/0018_auto_20181123_2111.py index 1fcd9a469d..8b8299fe2f 100644 --- a/src/press/migrations/0018_auto_20181123_2111.py +++ b/src/press/migrations/0018_auto_20181123_2111.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0017_auto_20181116_1144'), + ("press", "0017_auto_20181116_1144"), ] operations = [ migrations.AlterField( - model_name='press', - name='domain', - field=models.CharField(default='www.example.com', max_length=255, unique=True), + model_name="press", + name="domain", + field=models.CharField( + default="www.example.com", max_length=255, unique=True + ), ), ] diff --git a/src/press/migrations/0019_auto_20181218_1546.py b/src/press/migrations/0019_auto_20181218_1546.py index a8319e5637..8d71a8eaf3 100644 --- a/src/press/migrations/0019_auto_20181218_1546.py +++ b/src/press/migrations/0019_auto_20181218_1546.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0018_auto_20181123_2111'), + ("press", "0018_auto_20181123_2111"), ] operations = [ migrations.AlterField( - model_name='press', - name='is_secure', - field=models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.'), + model_name="press", + name="is_secure", + field=models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), ), ] diff --git a/src/press/migrations/0020_auto_20190319_1555.py b/src/press/migrations/0020_auto_20190319_1555.py index 85d6c09a16..c2f3fa75b0 100644 --- a/src/press/migrations/0020_auto_20190319_1555.py +++ b/src/press/migrations/0020_auto_20190319_1555.py @@ -8,30 +8,46 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0019_auto_20181218_1546'), + ("press", "0019_auto_20181218_1546"), ] operations = [ migrations.AlterField( - model_name='press', - name='enable_preprints', - field=models.BooleanField(default=False, help_text='Enables the repository system for this press.', verbose_name='Enable repository system',) + model_name="press", + name="enable_preprints", + field=models.BooleanField( + default=False, + help_text="Enables the repository system for this press.", + verbose_name="Enable repository system", + ), ), migrations.AlterField( - model_name='press', - name='footer_description', - field=models.TextField(blank=True, help_text='Additional HTML for the press footer.', null=True), + model_name="press", + name="footer_description", + field=models.TextField( + blank=True, help_text="Additional HTML for the press footer.", null=True + ), ), migrations.AlterField( - model_name='press', - name='password_length', - field=models.PositiveIntegerField(default=12, help_text='The minimum length of an account password.', validators=[django.core.validators.MinValueValidator(9)]), + model_name="press", + name="password_length", + field=models.PositiveIntegerField( + default=12, + help_text="The minimum length of an account password.", + validators=[django.core.validators.MinValueValidator(9)], + ), ), migrations.AlterField( - model_name='press', - name='thumbnail_image', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='press_thumbnail_image', to='core.File', verbose_name='Press Logo'), + model_name="press", + name="thumbnail_image", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="press_thumbnail_image", + to="core.File", + verbose_name="Press Logo", + ), ), ] diff --git a/src/press/migrations/0021_auto_20190329_1202.py b/src/press/migrations/0021_auto_20190329_1202.py index 3369b2fd3f..db01283b39 100644 --- a/src/press/migrations/0021_auto_20190329_1202.py +++ b/src/press/migrations/0021_auto_20190329_1202.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0020_auto_20190319_1555'), + ("press", "0020_auto_20190319_1555"), ] operations = [ migrations.AlterModelOptions( - name='press', - options={'verbose_name_plural': 'presses'}, + name="press", + options={"verbose_name_plural": "presses"}, ), ] diff --git a/src/press/migrations/0022_press_disable_journals.py b/src/press/migrations/0022_press_disable_journals.py index 8c1d0d10bf..b7fce114ea 100644 --- a/src/press/migrations/0022_press_disable_journals.py +++ b/src/press/migrations/0022_press_disable_journals.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0021_auto_20190329_1202'), + ("press", "0021_auto_20190329_1202"), ] operations = [ migrations.AddField( - model_name='press', - name='disable_journals', - field=models.BooleanField(default=False, help_text='If enabled, the journals page will no longer render.'), + model_name="press", + name="disable_journals", + field=models.BooleanField( + default=False, + help_text="If enabled, the journals page will no longer render.", + ), ), ] diff --git a/src/press/migrations/0022_press_privacy_policy_url.py b/src/press/migrations/0022_press_privacy_policy_url.py index 3e5a5c78c6..6f750f81aa 100644 --- a/src/press/migrations/0022_press_privacy_policy_url.py +++ b/src/press/migrations/0022_press_privacy_policy_url.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0021_auto_20190329_1202'), + ("press", "0021_auto_20190329_1202"), ] operations = [ migrations.AddField( - model_name='press', - name='privacy_policy_url', - field=models.URLField(blank=True, help_text='URL to an external privacy-policy, linked from the page footer. If blank, it links to the Janeway CMS page: /site/privacy.', max_length=999, null=True), + model_name="press", + name="privacy_policy_url", + field=models.URLField( + blank=True, + help_text="URL to an external privacy-policy, linked from the page footer. If blank, it links to the Janeway CMS page: /site/privacy.", + max_length=999, + null=True, + ), ), ] diff --git a/src/press/migrations/0023_auto_20200718_1117.py b/src/press/migrations/0023_auto_20200718_1117.py index 53c6137619..3d3e22698e 100644 --- a/src/press/migrations/0023_auto_20200718_1117.py +++ b/src/press/migrations/0023_auto_20200718_1117.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0022_press_disable_journals'), + ("press", "0022_press_disable_journals"), ] operations = [ migrations.AlterField( - model_name='press', - name='theme', - field=models.CharField(default='OLH', max_length=255), + model_name="press", + name="theme", + field=models.CharField(default="OLH", max_length=255), ), ] diff --git a/src/press/migrations/0023_auto_20210721_1213.py b/src/press/migrations/0023_auto_20210721_1213.py index 125d2e6428..a9eef4b4ba 100644 --- a/src/press/migrations/0023_auto_20210721_1213.py +++ b/src/press/migrations/0023_auto_20210721_1213.py @@ -6,19 +6,20 @@ def update_default_press_theme(apps, schema_editor): - Press = apps.get_model('press', 'Press') + Press = apps.get_model("press", "Press") for press in Press.objects.all(): - press.theme = 'OLH' + press.theme = "OLH" press.save() class Migration(migrations.Migration): - dependencies = [ - ('press', '0022_press_privacy_policy_url'), + ("press", "0022_press_privacy_policy_url"), ] operations = [ - migrations.RunPython(update_default_press_theme, reverse_code=migrations.RunPython.noop) + migrations.RunPython( + update_default_press_theme, reverse_code=migrations.RunPython.noop + ) ] diff --git a/src/press/migrations/0024_auto_20210729_0908.py b/src/press/migrations/0024_auto_20210729_0908.py index 51f406a191..5fbfcdc310 100644 --- a/src/press/migrations/0024_auto_20210729_0908.py +++ b/src/press/migrations/0024_auto_20210729_0908.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0023_auto_20210721_1213'), + ("press", "0023_auto_20210721_1213"), ] operations = [ migrations.AlterField( - model_name='press', - name='theme', - field=models.CharField(default='OLH', max_length=255), + model_name="press", + name="theme", + field=models.CharField(default="OLH", max_length=255), ), ] diff --git a/src/press/migrations/0024_update_press_template.py b/src/press/migrations/0024_update_press_template.py index 86ddc03a64..fe780a6f16 100644 --- a/src/press/migrations/0024_update_press_template.py +++ b/src/press/migrations/0024_update_press_template.py @@ -4,14 +4,17 @@ from django.db import migrations -REST_BASE = "{{ request.press_base_url }}{% url 'core_reset_password' reset_token.token %}" +REST_BASE = ( + "{{ request.press_base_url }}{% url 'core_reset_password' reset_token.token %}" +) RESET_NEW = "{% site_url 'core_reset_password' reset_token.token %}" REG_BASE = "{{ request.press_base_url }}{% url 'core_confirm_account' user.confirmation_code %}" REG_NEW = "{% site_url 'core_confirm_account' user.confirmation_code %}" + def update_press_templates(apps, schema_editor): - Press = apps.get_model('press', 'Press') + Press = apps.get_model("press", "Press") try: press_obj = Press.objects.all()[0] @@ -31,12 +34,11 @@ def update_press_templates(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('press', '0023_auto_20200718_1117'), + ("press", "0023_auto_20200718_1117"), ] operations = [ migrations.RunPython( - update_press_templates, - reverse_code=migrations.RunPython.noop + update_press_templates, reverse_code=migrations.RunPython.noop ) ] diff --git a/src/press/migrations/0025_auto_20200814_1433.py b/src/press/migrations/0025_auto_20200814_1433.py index bd9e319021..dc986f125d 100644 --- a/src/press/migrations/0025_auto_20200814_1433.py +++ b/src/press/migrations/0025_auto_20200814_1433.py @@ -6,25 +6,36 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0024_update_press_template'), + ("press", "0024_update_press_template"), ] operations = [ migrations.AlterField( - model_name='press', - name='password_reset_text', - field=models.TextField(blank=True, default='

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{% site_url \'core_reset_password\' reset_token.token %}

{{request.press.name}}

', null=True), + model_name="press", + name="password_reset_text", + field=models.TextField( + blank=True, + default="

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{% site_url 'core_reset_password' reset_token.token %}

{{request.press.name}}

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='preprint_publication', - field=models.TextField(blank=True, default='

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_publication", + field=models.TextField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='registration_text', - field=models.TextField(blank=True, default='

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{% site_url \'core_confirm_account\' user.confirmation_code %}
{{request.press.name}}

', null=True), + model_name="press", + name="registration_text", + field=models.TextField( + blank=True, + default="

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{% site_url 'core_confirm_account' user.confirmation_code %}
{{request.press.name}}

", + null=True, + ), ), ] diff --git a/src/press/migrations/0026_merge_20210811_1507.py b/src/press/migrations/0026_merge_20210811_1507.py index 2a5b5838b4..d6b6e456f1 100644 --- a/src/press/migrations/0026_merge_20210811_1507.py +++ b/src/press/migrations/0026_merge_20210811_1507.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0025_auto_20200814_1433'), - ('press', '0024_auto_20210729_0908'), + ("press", "0025_auto_20200814_1433"), + ("press", "0024_auto_20210729_0908"), ] - operations = [ - ] + operations = [] diff --git a/src/press/migrations/0027_auto_20220107_1219.py b/src/press/migrations/0027_auto_20220107_1219.py index f69402f40d..ec99c77900 100644 --- a/src/press/migrations/0027_auto_20220107_1219.py +++ b/src/press/migrations/0027_auto_20220107_1219.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0026_merge_20210811_1507'), + ("press", "0026_merge_20210811_1507"), ] operations = [ migrations.AlterField( - model_name='press', - name='domain', + model_name="press", + name="domain", field=models.CharField(blank=True, max_length=255, null=True, unique=True), ), ] diff --git a/src/press/migrations/0028_auto_20230308_2343.py b/src/press/migrations/0028_auto_20230308_2343.py index 5dca916ed7..ccdea2fbe0 100644 --- a/src/press/migrations/0028_auto_20230308_2343.py +++ b/src/press/migrations/0028_auto_20230308_2343.py @@ -6,20 +6,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0027_auto_20220107_1219'), + ("press", "0027_auto_20220107_1219"), ] operations = [ migrations.AddField( - model_name='press', - name='description', - field=models.TextField(blank=True, help_text='This will appear in web search results and on social media when the press URL is shared', null=True, verbose_name='Publisher description'), + model_name="press", + name="description", + field=models.TextField( + blank=True, + help_text="This will appear in web search results and on social media when the press URL is shared", + null=True, + verbose_name="Publisher description", + ), ), migrations.AlterField( - model_name='press', - name='footer_description', - field=models.TextField(blank=True, help_text='Additional HTML for the press footer.', null=True, verbose_name='Footer text'), + model_name="press", + name="footer_description", + field=models.TextField( + blank=True, + help_text="Additional HTML for the press footer.", + null=True, + verbose_name="Footer text", + ), ), ] diff --git a/src/press/migrations/0029_press_secondary_image.py b/src/press/migrations/0029_press_secondary_image.py index c600685f3f..8db4d792aa 100644 --- a/src/press/migrations/0029_press_secondary_image.py +++ b/src/press/migrations/0029_press_secondary_image.py @@ -7,19 +7,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0028_auto_20230308_2343'), + ("press", "0028_auto_20230308_2343"), ] operations = [ migrations.AddField( - model_name='press', - name='secondary_image', + model_name="press", + name="secondary_image", field=core.model_utils.SVGImageField( blank=True, - help_text='Optional secondary logo for footer. ' - 'Not implemented in all themes.', + help_text="Optional secondary logo for footer. " + "Not implemented in all themes.", null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=press.models.cover_images_upload_path, diff --git a/src/press/migrations/0030_press_secondary_image_url.py b/src/press/migrations/0030_press_secondary_image_url.py index 379e294931..08e3063539 100644 --- a/src/press/migrations/0030_press_secondary_image_url.py +++ b/src/press/migrations/0030_press_secondary_image_url.py @@ -4,15 +4,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0029_press_secondary_image'), + ("press", "0029_press_secondary_image"), ] operations = [ migrations.AddField( - model_name='press', - name='secondary_image_url', - field=models.URLField(blank=True, help_text='Turns secondary image into a link.', null=True), + model_name="press", + name="secondary_image_url", + field=models.URLField( + blank=True, help_text="Turns secondary image into a link.", null=True + ), ), ] diff --git a/src/press/migrations/0031_press_journal_footer_text.py b/src/press/migrations/0031_press_journal_footer_text.py index 1e8daa0ba6..06f2205363 100644 --- a/src/press/migrations/0031_press_journal_footer_text.py +++ b/src/press/migrations/0031_press_journal_footer_text.py @@ -4,15 +4,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0030_press_secondary_image_url'), + ("press", "0030_press_secondary_image_url"), ] operations = [ migrations.AddField( - model_name='press', - name='journal_footer_text', - field=models.TextField(blank=True, help_text='Text that will appear in the footer of every journal, to display publisher address or other essential info. ', null=True, verbose_name='Journal footer text'), + model_name="press", + name="journal_footer_text", + field=models.TextField( + blank=True, + help_text="Text that will appear in the footer of every journal, to display publisher address or other essential info. ", + null=True, + verbose_name="Journal footer text", + ), ), ] diff --git a/src/press/migrations/0031_staffgroup_staffgroupmember.py b/src/press/migrations/0031_staffgroup_staffgroupmember.py index fc868cd8ae..5625a79399 100644 --- a/src/press/migrations/0031_staffgroup_staffgroupmember.py +++ b/src/press/migrations/0031_staffgroup_staffgroupmember.py @@ -12,43 +12,73 @@ def default_press_id(): class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('press', '0031_press_journal_footer_text'), + ("press", "0031_press_journal_footer_text"), ] operations = [ migrations.CreateModel( - name='StaffGroup', + name="StaffGroup", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=500)), - ('description', models.TextField(blank=True)), - ('sequence', models.PositiveIntegerField()), - ('press', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to='press.press', - default=default_press_id, - )), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=500)), + ("description", models.TextField(blank=True)), + ("sequence", models.PositiveIntegerField()), + ( + "press", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="press.press", + default=default_press_id, + ), + ), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), migrations.CreateModel( - name='StaffGroupMember', + name="StaffGroupMember", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('job_title', models.CharField(blank=True, max_length=300)), - ('alternate_title', models.CharField(blank=True, max_length=300)), - ('publications', models.TextField(blank=True)), - ('sequence', models.PositiveIntegerField()), - ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='press.staffgroup')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("job_title", models.CharField(blank=True, max_length=300)), + ("alternate_title", models.CharField(blank=True, max_length=300)), + ("publications", models.TextField(blank=True)), + ("sequence", models.PositiveIntegerField()), + ( + "group", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="press.staffgroup", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), ] diff --git a/src/press/migrations/0032_auto_20231116_1057.py b/src/press/migrations/0032_auto_20231116_1057.py index 5e4d92b9e0..f15e4a1b1d 100644 --- a/src/press/migrations/0032_auto_20231116_1057.py +++ b/src/press/migrations/0032_auto_20231116_1057.py @@ -2,40 +2,60 @@ from django.db import migrations, models + def set_constraints_immediately(apps, schema_editor): """Circumvents psql not allowing transactions with DDL operations""" if schema_editor.connection.vendor.startswith("postgresql"): schema_editor.execute("SET CONSTRAINTS ALL IMMEDIATE") + def set_constraints_deferred(apps, schema_editor): if schema_editor.connection.vendor.startswith("postgresql"): schema_editor.execute("SET CONSTRAINTS ALL DEFERRED") class Migration(migrations.Migration): - dependencies = [ - ('press', '0031_press_journal_footer_text'), + ("press", "0031_press_journal_footer_text"), ] operations = [ - migrations.RunPython(set_constraints_immediately, reverse_code=set_constraints_deferred), + migrations.RunPython( + set_constraints_immediately, reverse_code=set_constraints_deferred + ), migrations.AlterField( - model_name='press', - name='description', - field=models.TextField(blank=True, default='', help_text='This will appear in web search results and on social media when the press URL is shared', verbose_name='Publisher description'), + model_name="press", + name="description", + field=models.TextField( + blank=True, + default="", + help_text="This will appear in web search results and on social media when the press URL is shared", + verbose_name="Publisher description", + ), preserve_default=False, ), migrations.AlterField( - model_name='press', - name='footer_description', - field=models.TextField(blank=True, default='', help_text='Additional HTML for the press footer.', verbose_name='Footer text'), + model_name="press", + name="footer_description", + field=models.TextField( + blank=True, + default="", + help_text="Additional HTML for the press footer.", + verbose_name="Footer text", + ), preserve_default=False, ), migrations.AlterField( - model_name='press', - name='journal_footer_text', - field=models.TextField(blank=True, default='', help_text='Text that will appear in the footer of every journal, to display publisher address or other essential info. ', verbose_name='Journal footer text'), + model_name="press", + name="journal_footer_text", + field=models.TextField( + blank=True, + default="", + help_text="Text that will appear in the footer of every journal, to display publisher address or other essential info. ", + verbose_name="Journal footer text", + ), preserve_default=False, ), - migrations.RunPython(set_constraints_deferred, reverse_code=set_constraints_immediately), + migrations.RunPython( + set_constraints_deferred, reverse_code=set_constraints_immediately + ), ] diff --git a/src/press/migrations/0033_auto_20240312_0922.py b/src/press/migrations/0033_auto_20240312_0922.py index 0c13c3dffc..6b09897a88 100644 --- a/src/press/migrations/0033_auto_20240312_0922.py +++ b/src/press/migrations/0033_auto_20240312_0922.py @@ -5,60 +5,91 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0032_auto_20231116_1057'), + ("press", "0032_auto_20231116_1057"), ] operations = [ migrations.AlterField( - model_name='press', - name='description', - field=core.model_utils.JanewayBleachField(blank=True, help_text='This will appear in web search results and on social media when the press URL is shared', verbose_name='Publisher description'), + model_name="press", + name="description", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="This will appear in web search results and on social media when the press URL is shared", + verbose_name="Publisher description", + ), ), migrations.AlterField( - model_name='press', - name='footer_description', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Additional HTML for the press footer.', verbose_name='Footer text'), + model_name="press", + name="footer_description", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Additional HTML for the press footer.", + verbose_name="Footer text", + ), ), migrations.AlterField( - model_name='press', - name='journal_footer_text', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Text that will appear in the footer of every journal, to display publisher address or other essential info. ', verbose_name='Journal footer text'), + model_name="press", + name="journal_footer_text", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Text that will appear in the footer of every journal, to display publisher address or other essential info. ", + verbose_name="Journal footer text", + ), ), migrations.AlterField( - model_name='press', - name='password_reset_text', - field=core.model_utils.JanewayBleachField(blank=True, default='

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{% site_url \'core_reset_password\' reset_token.token %}

{{request.press.name}}

', null=True), + model_name="press", + name="password_reset_text", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Dear {{ reset_token.account.full_name }},

A password reset for your account has been requested. You can reset your password at the following link:

{% site_url 'core_reset_password' reset_token.token %}

{{request.press.name}}

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='preprint_decline', - field=core.model_utils.JanewayBleachField(blank=True, default='

Dear {{ article.owner.full_name }},

Unfortunately we have decided not to publish your preprint.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_decline", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Unfortunately we have decided not to publish your preprint.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='preprint_publication', - field=core.model_utils.JanewayBleachField(blank=True, default='

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_publication", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Your preprint has been published on {{ request.press.name }}. It is now available on our website.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='preprint_start', + model_name="press", + name="preprint_start", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='press', - name='preprint_submission', - field=core.model_utils.JanewayBleachField(blank=True, default='

Dear {{ article.owner.full_name }},

Thank you for your preprint submission. Our Preprint Editor has been notified and will review it before making a decision.

Regards,

{{ request.press.name }} Team

', null=True), + model_name="press", + name="preprint_submission", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Dear {{ article.owner.full_name }},

Thank you for your preprint submission. Our Preprint Editor has been notified and will review it before making a decision.

Regards,

{{ request.press.name }} Team

", + null=True, + ), ), migrations.AlterField( - model_name='press', - name='preprints_about', + model_name="press", + name="preprints_about", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='press', - name='registration_text', - field=core.model_utils.JanewayBleachField(blank=True, default='

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{% site_url \'core_confirm_account\' user.confirmation_code %}
{{request.press.name}}

', null=True), + model_name="press", + name="registration_text", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Dear {{ user.full_name }}

Thank you for registering for {{ request.press.name }}. You can confirm your account at the following link:


{% site_url 'core_confirm_account' user.confirmation_code %}
{{request.press.name}}

", + null=True, + ), ), ] diff --git a/src/press/migrations/0033_merge_20240201_1108.py b/src/press/migrations/0033_merge_20240201_1108.py index 481ce46b97..270ac6b388 100644 --- a/src/press/migrations/0033_merge_20240201_1108.py +++ b/src/press/migrations/0033_merge_20240201_1108.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0031_staffgroup_staffgroupmember'), - ('press', '0032_auto_20231116_1057'), + ("press", "0031_staffgroup_staffgroupmember"), + ("press", "0032_auto_20231116_1057"), ] - operations = [ - ] + operations = [] diff --git a/src/press/migrations/0034_merge_20240315_1649.py b/src/press/migrations/0034_merge_20240315_1649.py index 3dddd41551..c1e9200ed3 100644 --- a/src/press/migrations/0034_merge_20240315_1649.py +++ b/src/press/migrations/0034_merge_20240315_1649.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0033_auto_20240312_0922'), - ('press', '0033_merge_20240201_1108'), + ("press", "0033_auto_20240312_0922"), + ("press", "0033_merge_20240201_1108"), ] - operations = [ - ] + operations = [] diff --git a/src/press/migrations/0035_alter_staffgroup_press.py b/src/press/migrations/0035_alter_staffgroup_press.py index 665e822098..f4b28c2ffc 100644 --- a/src/press/migrations/0035_alter_staffgroup_press.py +++ b/src/press/migrations/0035_alter_staffgroup_press.py @@ -6,19 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0034_merge_20240315_1649'), + ("press", "0034_merge_20240315_1649"), ] operations = [ migrations.AlterField( - model_name='staffgroup', - name='press', + model_name="staffgroup", + name="press", field=models.ForeignKey( default=core.model_utils.default_press_id, on_delete=django.db.models.deletion.CASCADE, - to='press.press', + to="press.press", ), ), ] diff --git a/src/press/models.py b/src/press/models.py index 20c19ff192..68c56b4ccc 100755 --- a/src/press/models.py +++ b/src/press/models.py @@ -37,7 +37,7 @@ def cover_images_upload_path(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -47,21 +47,20 @@ def cover_images_upload_path(instance, filename): def press_carousel_choices(): return ( - ('articles', 'Latest Articles'), - ('news', 'Latest News'), - ('news_and_articles', 'Latest News and Articles') + ("articles", "Latest Articles"), + ("news", "Latest News"), + ("news_and_articles", "Latest News and Articles"), ) def press_text(type): - path = os.path.join( - settings.BASE_DIR, 'utils', 'install', 'press_text.json') - with open(path, 'r', encoding="utf-8") as f: + path = os.path.join(settings.BASE_DIR, "utils", "install", "press_text.json") + with open(path, "r", encoding="utf-8") as f: text = json.loads(f.read())[0] - if type == 'registration': - return text.get('registration') - elif type == 'reset': - return text.get('reset') + if type == "registration": + return text.get("registration") + elif type == "reset": + return text.get("reset") else: return text.get(type) @@ -69,102 +68,129 @@ def press_text(type): class Press(AbstractSiteModel): name = models.CharField(max_length=600) thumbnail_image = models.ForeignKey( - 'core.File', + "core.File", null=True, blank=True, - related_name='press_thumbnail_image', - verbose_name='Press Logo', + related_name="press_thumbnail_image", + verbose_name="Press Logo", on_delete=models.SET_NULL, ) description = JanewayBleachField( blank=True, - verbose_name='Publisher description', - help_text='This will appear in web search results and on social media when the press URL is shared', + verbose_name="Publisher description", + help_text="This will appear in web search results and on social media when the press URL is shared", ) footer_description = JanewayBleachField( blank=True, - verbose_name='Footer text', - help_text='Additional HTML for the press footer.', + verbose_name="Footer text", + help_text="Additional HTML for the press footer.", ) journal_footer_text = JanewayBleachField( blank=True, - verbose_name='Journal footer text', - help_text='Text that will appear in the footer ' - 'of every journal, to display publisher ' - 'address or other essential info. ', + verbose_name="Journal footer text", + help_text="Text that will appear in the footer " + "of every journal, to display publisher " + "address or other essential info. ", ) secondary_image = SVGImageField( upload_to=cover_images_upload_path, null=True, blank=True, storage=fs, - help_text='Optional secondary logo for footer. ' - 'Not implemented in all themes.', + help_text="Optional secondary logo for footer. " + "Not implemented in all themes.", ) secondary_image_url = models.URLField( null=True, blank=True, - help_text='Turns secondary image into a link.', + help_text="Turns secondary image into a link.", ) - main_contact = models.EmailField(default='janeway@voyager.com', blank=False, null=False) - theme = models.CharField(max_length=255, default='OLH', blank=False, null=False) + main_contact = models.EmailField( + default="janeway@voyager.com", blank=False, null=False + ) + theme = models.CharField(max_length=255, default="OLH", blank=False, null=False) homepage_news_items = models.PositiveIntegerField(default=5) - carousel_type = models.CharField(max_length=30, default='articles', choices=press_carousel_choices()) + carousel_type = models.CharField( + max_length=30, default="articles", choices=press_carousel_choices() + ) carousel_items = models.PositiveIntegerField(default=4) carousel = models.OneToOneField( - 'carousel.Carousel', - related_name='press', + "carousel.Carousel", + related_name="press", null=True, blank=True, on_delete=models.SET_NULL, ) - default_carousel_image = models.ImageField(upload_to=cover_images_upload_path, null=True, blank=True, storage=fs) - favicon = models.ImageField(upload_to=cover_images_upload_path, null=True, blank=True, storage=fs) + default_carousel_image = models.ImageField( + upload_to=cover_images_upload_path, null=True, blank=True, storage=fs + ) + favicon = models.ImageField( + upload_to=cover_images_upload_path, null=True, blank=True, storage=fs + ) random_featured_journals = models.BooleanField(default=False) - featured_journals = models.ManyToManyField('journal.Journal', blank=True, null=True) - carousel_news_items = models.ManyToManyField('comms.NewsItem', blank=True, null=True) + featured_journals = models.ManyToManyField("journal.Journal", blank=True, null=True) + carousel_news_items = models.ManyToManyField( + "comms.NewsItem", blank=True, null=True + ) tracking_code = models.TextField(blank=True, null=True) privacy_policy_url = models.URLField( - max_length=999, blank=True, null=True, + max_length=999, + blank=True, + null=True, help_text="URL to an external privacy-policy, linked from the page" " footer. If blank, it links to the Janeway CMS page: /site/privacy.", ) - password_reset_text = JanewayBleachField(blank=True, null=True, default=press_text('reset')) - registration_text = JanewayBleachField(blank=True, null=True, default=press_text('registration')) + password_reset_text = JanewayBleachField( + blank=True, null=True, default=press_text("reset") + ) + registration_text = JanewayBleachField( + blank=True, null=True, default=press_text("registration") + ) - password_number = models.BooleanField(default=False, help_text='If set, passwords must include one number.') - password_upper = models.BooleanField(default=False, help_text='If set, passwords must include one upper case.') + password_number = models.BooleanField( + default=False, help_text="If set, passwords must include one number." + ) + password_upper = models.BooleanField( + default=False, help_text="If set, passwords must include one upper case." + ) password_length = models.PositiveIntegerField( default=12, validators=[MinValueValidator(9)], - help_text='The minimum length of an account password.', + help_text="The minimum length of an account password.", ) enable_preprints = models.BooleanField( default=False, - help_text='Enables the repository system for this press.', - verbose_name='Enable repository system', + help_text="Enables the repository system for this press.", + verbose_name="Enable repository system", ) preprints_about = JanewayBleachField(blank=True, null=True) preprint_start = JanewayBleachField(blank=True, null=True) - preprint_pdf_only = models.BooleanField(default=True, help_text='Forces manuscript files to be PDFs for Preprints.') - preprint_submission = JanewayBleachField(blank=True, null=True, default=press_text('submission')) - preprint_publication = JanewayBleachField(blank=True, null=True, default=press_text('publication')) - preprint_decline = JanewayBleachField(blank=True, null=True, default=press_text('decline')) + preprint_pdf_only = models.BooleanField( + default=True, help_text="Forces manuscript files to be PDFs for Preprints." + ) + preprint_submission = JanewayBleachField( + blank=True, null=True, default=press_text("submission") + ) + preprint_publication = JanewayBleachField( + blank=True, null=True, default=press_text("publication") + ) + preprint_decline = JanewayBleachField( + blank=True, null=True, default=press_text("decline") + ) random_homepage_preprints = models.BooleanField(default=False) - homepage_preprints = models.ManyToManyField('submission.Article', blank=True) + homepage_preprints = models.ManyToManyField("submission.Article", blank=True) disable_journals = models.BooleanField( - default=False, - help_text='If enabled, the journals page will no longer render.' + default=False, help_text="If enabled, the journals page will no longer render." ) def __str__(self): - return u'%s' % self.name + return "%s" % self.name def __repr__(self): - return u'%s' % self.name + return "%s" % self.name @staticmethod def get_press(request): @@ -177,6 +203,7 @@ def get_press(request): @staticmethod def journals(**filters): from journal import models as journal_models + if filters: return journal_models.Journal.objects.filter(**filters) return journal_models.Journal.objects.all() @@ -191,6 +218,7 @@ def issues(self, **filters): filters = {} filters["journal__press"] = self from journal import models as journal_models + if filters: return journal_models.Issue.objects.filter(**filters) return journal_models.Journal.objects.all() @@ -200,14 +228,14 @@ def users(): return core_models.Account.objects.all() def journal_path_url(self, journal, path=None): - """ Returns a Journal's path mode url relative to its press """ + """Returns a Journal's path mode url relative to its press""" return self.site_path_url(journal, path) def repository_path_url(self, repository, path=None): - """ Returns a Repo's path mode url relative to its press """ + """Returns a Repo's path mode url relative to its press""" return self.site_path_url(repository, path) - def site_path_url(self, child_site, path=None, query=''): + def site_path_url(self, child_site, path=None, query=""): """Returns the path mode URL of a site relative to its press""" _path = "/" + child_site.code request = logic.get_current_request() @@ -218,7 +246,7 @@ def site_path_url(self, child_site, path=None, query=''): if path is not None: # Ignore duplicate site code if provided in code if path.startswith(_path): - path = path[len(_path):] + path = path[len(_path) :] _path += path return logic.build_url( @@ -233,16 +261,22 @@ def site_path_url(self, child_site, path=None, query=''): def press_cover(request, absolute=True): if request.press.thumbnail_image: if absolute: - return os.path.join(settings.BASE_DIR, 'files', 'press', - str(request.press.thumbnail_image.uuid_filename)) + return os.path.join( + settings.BASE_DIR, + "files", + "press", + str(request.press.thumbnail_image.uuid_filename), + ) else: - return os.path.join('files', 'press', str(request.press.thumbnail_image.uuid_filename)) + return os.path.join( + "files", "press", str(request.press.thumbnail_image.uuid_filename) + ) else: return None @staticmethod def install_cover(press, request): - """ Installs the default cover for the press (stored in Files/press/cover.png) + """Installs the default cover for the press (stored in Files/press/cover.png) :param press: the press object :param request: the current request or None @@ -250,7 +284,11 @@ def install_cover(press, request): """ if request: - owner = request.user if request.user is not None and not request.user.is_anonymous else core_models.Account(id=1) + owner = ( + request.user + if request.user is not None and not request.user.is_anonymous + else core_models.Account(id=1) + ) else: owner = core_models.Account(id=1) @@ -260,7 +298,7 @@ def install_cover(press, request): uuid_filename="cover.png", label="Press logo", description="Logo for the press", - owner=owner + owner=owner, ) core_models.File.add_root(instance=thumbnail_file) @@ -270,7 +308,10 @@ def install_cover(press, request): def next_journal_order(self): from journal import models as journal_models - max_number = max([journal.sequence for journal in journal_models.Journal.objects.all()]) + + max_number = max( + [journal.sequence for journal in journal_models.Journal.objects.all()] + ) if not max_number: return 0 @@ -278,13 +319,15 @@ def next_journal_order(self): return max_number + 1 def next_contact_order(self): - contacts = core_models.Contacts.objects.filter(content_type__model='press', object_id=self.pk) + contacts = core_models.Contacts.objects.filter( + content_type__model="press", object_id=self.pk + ) orderings = [contact.sequence for contact in contacts] return max(orderings) + 1 if orderings else 0 @property def active_carousel(self): - """ Renders a carousel for the press homepage. + """Renders a carousel for the press homepage. :return: a tuple containing the active carousel and list of associated articles """ if self.carousel is None or not self.carousel.enabled: @@ -300,7 +343,7 @@ def get_setting_value(self, name): try: return PressSetting.objects.get(press=self, name=name).value except PressSetting.DoesNotExist: - return '' + return "" @property def publishes_conferences(self): @@ -313,6 +356,7 @@ def publishes_journals(self): @cache(600) def live_repositories(self): from repository import models as repository_models + return repository_models.Repository.objects.filter( live=True, ) @@ -320,6 +364,7 @@ def live_repositories(self): @cache(600) def preprint_editors(self): from repository import models as repository_models + editors = list() subjects = repository_models.Subject.objects.all() @@ -348,19 +393,19 @@ def navigation_items(self): @property def code(self): - return 'press' + return "press" @property def public_journals(self): - Journal = apps.get_model('journal.Journal') + Journal = apps.get_model("journal.Journal") return Journal.objects.filter( hide_from_press=False, is_conference=False, - ).order_by('sequence') + ).order_by("sequence") @property def published_articles(self): - Article = apps.get_model('submission.Article') + Article = apps.get_model("submission.Article") return Article.objects.filter( stage=submission_models.STAGE_PUBLISHED, date_published__lte=timezone.now(), @@ -371,7 +416,7 @@ def next_group_order(self): return max(orderings) + 1 if orderings else 0 class Meta: - verbose_name_plural = 'presses' + verbose_name_plural = "presses" class PressSetting(models.Model): @@ -384,7 +429,7 @@ class PressSetting(models.Model): is_boolean = models.BooleanField(default=False) def __str__(self): - return '{name} - {press}'.format(name=self.name, press=self.press.name) + return "{name} - {press}".format(name=self.name, press=self.press.name) class StaffGroup(models.Model): @@ -401,10 +446,10 @@ def members(self): return self.staffgroupmember_set.all() class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): - return f'{self.name}, {self.press.name}' + return f"{self.name}, {self.press.name}" class StaffGroupMember(models.Model): @@ -413,7 +458,7 @@ class StaffGroupMember(models.Model): on_delete=models.CASCADE, ) user = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) job_title = models.CharField(max_length=300, blank=True) @@ -422,7 +467,7 @@ class StaffGroupMember(models.Model): sequence = models.PositiveIntegerField() class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): - return f'{self.user} in {self.group}' + return f"{self.user} in {self.group}" diff --git a/src/press/templatetags/press_url.py b/src/press/templatetags/press_url.py index 11c1245a08..d6f89b5265 100755 --- a/src/press/templatetags/press_url.py +++ b/src/press/templatetags/press_url.py @@ -28,10 +28,10 @@ def svg(filename): mimetype = mimetypes.guess_type(path, strict=True) - if not mimetype or mimetype[0] != 'image/svg+xml': + if not mimetype or mimetype[0] != "image/svg+xml": return mark_safe( ''.format( - url=reverse('press_cover_download'), + url=reverse("press_cover_download"), ) ) @@ -63,7 +63,7 @@ def svg_or_image(image_field, css_class="", alt_text="", inline=False): mimetype = mimetypes.guess_type(image_field.path, strict=True) - if not inline or not mimetype or mimetype[0] != 'image/svg+xml': + if not inline or not mimetype or mimetype[0] != "image/svg+xml": return mark_safe( '{alt_text}'.format( url=image_field.url, @@ -78,4 +78,3 @@ def svg_or_image(image_field, css_class="", alt_text="", inline=False): except FileNotFoundError: logger.warning("Could not read SVG file %s", image_field.path) return None - diff --git a/src/press/utils.py b/src/press/utils.py index acd5034e2c..336a14571f 100644 --- a/src/press/utils.py +++ b/src/press/utils.py @@ -14,4 +14,4 @@ def get_navigation_items(press): content_type=content_type, object_id=press.pk, top_level_nav__isnull=True, - ).order_by('sequence') + ).order_by("sequence") diff --git a/src/press/views.py b/src/press/views.py index 17dceb5bc5..2fd816ec03 100755 --- a/src/press/views.py +++ b/src/press/views.py @@ -59,14 +59,14 @@ def index(request): template = "press/press_index.html" context = { - 'homepage_elements': homepage_elements, + "homepage_elements": homepage_elements, } # call all registered plugin block hooks to get relevant contexts - for hook in settings.PLUGIN_HOOKS.get('yield_homepage_element_context', []): - if hook.get('name') in homepage_element_names: - hook_module = plugin_loader.import_module(hook.get('module')) - function = getattr(hook_module, hook.get('function')) + for hook in settings.PLUGIN_HOOKS.get("yield_homepage_element_context", []): + if hook.get("name") in homepage_element_names: + hook_module = plugin_loader.import_module(hook.get("module")) + function = getattr(hook_module, hook.get("function")) element_context = function(request, homepage_elements) for k, v in element_context.items(): @@ -89,10 +89,9 @@ def sitemap(request): # if there is a repository we return the repository sitemap. return repository_views.sitemap(request) - return core_views.sitemap( request, - ['sitemap.xml'], + ["sitemap.xml"], ) @@ -101,7 +100,7 @@ def robots(request): Serves a generated robots.txt. """ try: - if settings.URL_CONFIG == 'domain' and request.journal or request.repository: + if settings.URL_CONFIG == "domain" and request.journal or request.repository: if request.journal and request.journal.domain: return files.serve_robots_file(journal=request.journal) elif request.repository and request.repository.domain: @@ -111,7 +110,7 @@ def robots(request): raise Http404() return files.serve_robots_file() except FileNotFoundError: - logger.warning('Robots file not found.') + logger.warning("Robots file not found.") raise Http404() @@ -126,7 +125,7 @@ def journals(request): template = "press/press_journals.html" context = { - 'journals': request.press.public_journals, + "journals": request.press.public_journals, } return render(request, template, context) @@ -141,11 +140,11 @@ def conferences(request): template = "press/press_journals.html" journal_objects = journal_models.Journal.objects.filter( - hide_from_press=False, - is_conference=True, - ).order_by('sequence') + hide_from_press=False, + is_conference=True, + ).order_by("sequence") - context = {'journals': journal_objects} + context = {"journals": journal_objects} return render(request, template, context) @@ -165,46 +164,46 @@ def manager_index(request): if request.POST: form = journal_forms.JournalForm(request.POST) - modal = 'new_journal' + modal = "new_journal" if form.is_valid(): new_journal = form.save() new_journal.sequence = request.press.next_journal_order() new_journal.save() - call_command('install_plugins') + call_command("install_plugins") install.update_issue_types(new_journal) new_journal.setup_directory() return redirect( new_journal.site_url( path=reverse( - 'core_edit_settings_group', + "core_edit_settings_group", kwargs={ - 'display_group': 'journal', - } + "display_group": "journal", + }, ) ) ) support_message = core_logic.render_nested_setting( - 'support_contact_message_for_staff', - 'general', + "support_contact_message_for_staff", + "general", request, - nested_settings=[('support_email','general')], + nested_settings=[("support_email", "general")], ) published_articles = submission_models.Article.objects.filter( stage=submission_models.STAGE_PUBLISHED, journal__isnull=False, - ).select_related('journal')[:50] + ).select_related("journal")[:50] - template = 'press/press_manager_index.html' + template = "press/press_manager_index.html" context = { - 'journals': journal_models.Journal.objects.all().order_by('sequence'), - 'form': form, - 'modal': modal, - 'published_articles': published_articles, - 'version': version, - 'repositories': models.Repository.objects.all(), - 'url_config': settings.URL_CONFIG, - 'support_message': support_message, + "journals": journal_models.Journal.objects.all().order_by("sequence"), + "form": form, + "modal": modal, + "published_articles": published_articles, + "version": version, + "repositories": models.Repository.objects.all(), + "url_config": settings.URL_CONFIG, + "support_message": support_message, } return render(request, template, context) @@ -221,8 +220,7 @@ def edit_press(request): press = request.press form = forms.PressForm( - instance=press, - initial={'press_logo': press.thumbnail_image} + instance=press, initial={"press_logo": press.thumbnail_image} ) if request.POST: @@ -232,16 +230,19 @@ def edit_press(request): if press.default_carousel_image: from core import logic as core_logic - core_logic.resize_and_crop(press.default_carousel_image.path, [750, 324], 'middle') - messages.add_message(request, messages.INFO, 'Press updated.') + core_logic.resize_and_crop( + press.default_carousel_image.path, [750, 324], "middle" + ) + + messages.add_message(request, messages.INFO, "Press updated.") - return redirect(reverse('press_edit_press')) + return redirect(reverse("press_edit_press")) - template = 'press/edit_press.html' + template = "press/edit_press.html" context = { - 'press': press, - 'form': form, + "press": press, + "form": form, } return render(request, template, context) @@ -276,7 +277,7 @@ def serve_press_file(request, file_id): if file.article_id: raise Http404 - path_parts = ('press',) + path_parts = ("press",) response = files.serve_any_file( request, @@ -298,17 +299,13 @@ def journal_order(request): journals = journal_models.Journal.objects.all() - ids = [int(_id) for _id in request.POST.getlist('journal[]')] + ids = [int(_id) for _id in request.POST.getlist("journal[]")] for journal in journals: sequence = ids.index(journal.pk) - journal_models.Journal.objects.filter( - pk=journal.pk - ).update( - sequence=sequence - ) + journal_models.Journal.objects.filter(pk=journal.pk).update(sequence=sequence) - return HttpResponse('Thanks') + return HttpResponse("Thanks") @staff_member_required @@ -316,19 +313,19 @@ def journal_domain(request, journal_id): journal = get_object_or_404(journal_models.Journal, pk=journal_id) if request.POST: - new_domain = request.POST.get('domain', None) + new_domain = request.POST.get("domain", None) journal.domain = new_domain journal.save() - return redirect(reverse('core_manager_index')) + return redirect(reverse("core_manager_index")) if new_domain: - messages.add_message(request, messages.SUCCESS, 'Domain updated') + messages.add_message(request, messages.SUCCESS, "Domain updated") else: - messages.add_message(request, messages.WARNING, 'No domain set') + messages.add_message(request, messages.WARNING, "No domain set") - template = 'press/journal_domain.html' + template = "press/journal_domain.html" context = { - 'journal': journal, + "journal": journal, } return render(request, template, context) @@ -338,44 +335,47 @@ def journal_domain(request, journal_id): def merge_users(request): users = core_models.Account.objects.none() - get_from = request.GET.get('from') - get_to = request.GET.get('to') + get_from = request.GET.get("from") + get_to = request.GET.get("to") if request.POST: from_id = request.POST["from"] to_id = request.POST["to"] if from_id == to_id: messages.add_message( - request, messages.ERROR, + request, + messages.ERROR, "Can't merge a user with itself", ) - return redirect(reverse('merge_users')) + return redirect(reverse("merge_users")) try: from_acc = core_models.Account.objects.get(id=from_id) to_acc = core_models.Account.objects.get(id=to_id) except core_models.Account.DoesNotExist: messages.add_message( - request, messages.ERROR, + request, + messages.ERROR, "Can't find users with ids %d, %d" % (from_id, to_id), ) merge_models(from_acc, to_acc) messages.add_message( - request, messages.INFO, + request, + messages.INFO, "Merged %s into %s" % (from_acc.username, to_acc.username), ) - return redirect(reverse('merge_users')) + return redirect(reverse("merge_users")) template = "press/merge_users.html" context = { - 'users': users, + "users": users, } return render(request, template, context) -@method_decorator(staff_member_required, name='dispatch') +@method_decorator(staff_member_required, name="dispatch") class IdentifierManager(identifier_views.IdentifierManager): - template_name = 'core/manager/identifier_manager.html' + template_name = "core/manager/identifier_manager.html" @staff_member_required @@ -387,22 +387,20 @@ def edit_press_journal_description(request, journal_id): journal_models.Journal, pk=journal_id, ) - form = forms.PressJournalDescription( - journal=journal - ) + form = forms.PressJournalDescription(journal=journal) if request.POST: fire_redirect = False - if 'clear' in request.POST: + if "clear" in request.POST: setting_handler.save_setting( - setting_group_name='general', - setting_name='press_journal_description', + setting_group_name="general", + setting_name="press_journal_description", journal=journal, - value='', + value="", ) messages.add_message( request, messages.INFO, - _('Description deleted.'), + _("Description deleted."), ) fire_redirect = True else: @@ -415,22 +413,22 @@ def edit_press_journal_description(request, journal_id): messages.add_message( request, messages.INFO, - _('Description saved.'), + _("Description saved."), ) fire_redirect = True if fire_redirect: return redirect( reverse( - 'edit_press_journal_description', - kwargs={'journal_id': journal.pk}, + "edit_press_journal_description", + kwargs={"journal_id": journal.pk}, ) ) context = { - 'journal': journal, - 'form': form, + "journal": journal, + "form": form, } - template = 'press/edit_press_journal_description.html' + template = "press/edit_press_journal_description.html" return render( request, template, @@ -438,6 +436,6 @@ def edit_press_journal_description(request, journal_id): ) -@method_decorator(staff_member_required, name='dispatch') +@method_decorator(staff_member_required, name="dispatch") class AllUsers(BaseUserList): pass diff --git a/src/production/admin.py b/src/production/admin.py index 659bb3eae4..9ae90aa1c0 100755 --- a/src/production/admin.py +++ b/src/production/admin.py @@ -8,16 +8,33 @@ class ProductionAssignmentAdmin(admin.ModelAdmin): - list_display = ('pk', 'article', 'production_manager', 'editor', 'assigned', 'closed', 'accepted_by_manager') - list_filter = ('article', 'production_manager', 'editor') - raw_id_fields = ('article', 'production_manager', 'editor', 'accepted_by_manager') + list_display = ( + "pk", + "article", + "production_manager", + "editor", + "assigned", + "closed", + "accepted_by_manager", + ) + list_filter = ("article", "production_manager", "editor") + raw_id_fields = ("article", "production_manager", "editor", "accepted_by_manager") class TypesetTaskAdmin(admin.ModelAdmin): - list_display = ('pk', 'assignment', 'typesetter', 'assigned', 'notified', 'accepted', 'completed', 'editor_reviewed') - list_filter = ('assignment', 'typesetter', 'notified', 'editor_reviewed') - raw_id_fields = ('assignment', 'typesetter') - filter_horizontal = ('files_for_typesetting', 'galleys_loaded') + list_display = ( + "pk", + "assignment", + "typesetter", + "assigned", + "notified", + "accepted", + "completed", + "editor_reviewed", + ) + list_filter = ("assignment", "typesetter", "notified", "editor_reviewed") + raw_id_fields = ("assignment", "typesetter") + filter_horizontal = ("files_for_typesetting", "galleys_loaded") admin_list = [ diff --git a/src/production/forms.py b/src/production/forms.py index 4ef0263fd4..ee1c0a2f6c 100755 --- a/src/production/forms.py +++ b/src/production/forms.py @@ -13,30 +13,29 @@ class TypesetterNote(forms.ModelForm): class Meta: model = models.TypesetTask - fields = ('note_from_typesetter',) + fields = ("note_from_typesetter",) class AssignTypesetter(forms.ModelForm): - def __init__(self, *args, **kwargs): - self.copyedit_files = kwargs.pop('files') - self.article = kwargs.pop('article') - self.typesetters = kwargs.pop('typesetters') - self.assignment = kwargs.pop('assignment') + self.copyedit_files = kwargs.pop("files") + self.article = kwargs.pop("article") + self.typesetters = kwargs.pop("typesetters") + self.assignment = kwargs.pop("assignment") super(AssignTypesetter, self).__init__(*args, **kwargs) class Meta: model = models.TypesetTask fields = ( - 'typesetter', - 'files_for_typesetting', - 'due', - 'typeset_task', + "typesetter", + "files_for_typesetting", + "due", + "typeset_task", ) widgets = { - 'due': HTMLDateInput(), + "due": HTMLDateInput(), } def clean(self): @@ -45,20 +44,17 @@ def clean(self): file_check = logic.check_posted_typesetter_files( self.article, self.copyedit_files, - cleaned_data.get('files_for_typesetting'), + cleaned_data.get("files_for_typesetting"), ) if not file_check: - self.add_error( - 'files_for_typesetting', - 'File not found in files list.' - ) + self.add_error("files_for_typesetting", "File not found in files list.") - if not cleaned_data.get('typesetter') in logic.typesetter_users( - self.typesetters): + if not cleaned_data.get("typesetter") in logic.typesetter_users( + self.typesetters + ): self.add_error( - 'typesetter', - 'Typesetter chosen not in list of typesetters.' + "typesetter", "Typesetter chosen not in list of typesetters." ) def save(self, commit=True): @@ -68,7 +64,7 @@ def save(self, commit=True): if commit: task.save() - for file in self.cleaned_data.get('files_for_typesetting'): + for file in self.cleaned_data.get("files_for_typesetting"): task.files_for_typesetting.add(file) return task @@ -80,13 +76,13 @@ class GalleyForm(forms.ModelForm): class Meta: model = core_models.Galley fields = ( - 'label', - 'public', + "label", + "public", ) def __init__(self, *args, **kwargs): - include_file = kwargs.pop('include_file', True) + include_file = kwargs.pop("include_file", True) super().__init__(*args, **kwargs) - self.fields['label'].required = False + self.fields["label"].required = False if not include_file: - self.fields.pop('file') + self.fields.pop("file") diff --git a/src/production/logic.py b/src/production/logic.py index 140da30b61..dd2d42da9e 100755 --- a/src/production/logic.py +++ b/src/production/logic.py @@ -25,18 +25,24 @@ def get_production_managers(article): production_assignments = models.ProductionAssignment.objects.filter(article=article) - production_managers = [assignment.copyeditor.pk for assignment in production_assignments] + production_managers = [ + assignment.copyeditor.pk for assignment in production_assignments + ] - return core_models.AccountRole.objects.filter(role__slug='production').exclude(user__pk__in=production_managers) + return core_models.AccountRole.objects.filter(role__slug="production").exclude( + user__pk__in=production_managers + ) def get_typesetters(article): - typeset_assignments = models.TypesetTask.objects.filter(assignment__article=article, - completed__isnull=True) + typeset_assignments = models.TypesetTask.objects.filter( + assignment__article=article, completed__isnull=True + ) typesetters = [task.typesetter.pk for task in typeset_assignments] - return core_models.AccountRole.objects.filter(role__slug='typesetter', journal=article.journal).exclude( - user__pk__in=typesetters) + return core_models.AccountRole.objects.filter( + role__slug="typesetter", journal=article.journal + ).exclude(user__pk__in=typesetters) def get_all_galleys(article): @@ -64,7 +70,7 @@ def save_supp_file(article, request, uploaded_file, label): article.supplementary_files.add(supp_file) -def save_source_file(article, request, uploaded_file, label='Source File'): +def save_source_file(article, request, uploaded_file, label="Source File"): new_file = files.save_file_to_article(uploaded_file, article, request.user) new_file.label = label new_file.is_galley = False @@ -79,24 +85,32 @@ def get_galley_label_and_type(file): file eg ('HTML', 'html') or ('XML', 'xml') where a file mime doesn't match a supported Janeway file type it returns ('Other', 'other'). """ - label = 'Other' - type_ = 'other' + label = "Other" + type_ = "other" if file.mime_type in files.HTML_MIMETYPES: - type_ = 'html' - label = 'HTML' + type_ = "html" + label = "HTML" elif file.mime_type in files.XML_MIMETYPES: - type_ = 'xml' - label = 'XML' + type_ = "xml" + label = "XML" elif file.mime_type in files.PDF_MIMETYPES: - type_ = 'pdf' - label = 'PDF' + type_ = "pdf" + label = "PDF" elif file.mime_type in files.IMAGE_MIMETYPES: - type_ = 'image' - label = 'Image' + type_ = "image" + label = "Image" return label, type_ -def save_galley(article, request, uploaded_file, is_galley, label=None, save_to_disk=True, public=True): +def save_galley( + article, + request, + uploaded_file, + is_galley, + label=None, + save_to_disk=True, + public=True, +): if isinstance(uploaded_file, str): mime = files.file_path_mime(uploaded_file) else: @@ -122,7 +136,7 @@ def save_galley(article, request, uploaded_file, is_galley, label=None, save_to_ galley_label = label if new_file.mime_type in files.HTML_MIMETYPES: - with open(new_file.self_article_path(), 'r+', encoding="utf-8") as f: + with open(new_file.self_article_path(), "r+", encoding="utf-8") as f: html_contents = f.read() f.seek(0) cleaned_html = remove_css_from_html(html_contents) @@ -144,7 +158,7 @@ def save_galley(article, request, uploaded_file, is_galley, label=None, save_to_ def remove_css_from_html(source_html): - """ Removes any embedded css from the given html + """Removes any embedded css from the given html :param html: a str of containing html to be cleaned :return: A str with the cleaned html """ @@ -161,7 +175,7 @@ def remove_css_from_html(source_html): # Remove internal stylesheets for tag in soup(): - del tag["style"] + del tag["style"] return str(soup) @@ -171,19 +185,19 @@ def replace_galley_file(article, request, galley, uploaded_file): files.overwrite_file( uploaded_file, galley.file, - ('articles', article.pk), + ("articles", article.pk), ) else: - messages.add_message(request, messages.WARNING, 'No file was selected.') + messages.add_message(request, messages.WARNING, "No file was selected.") def save_galley_image( - galley, - request, - uploaded_file, - label="Galley Image", - fixed=False, - check_for_existing_images=False, + galley, + request, + uploaded_file, + label="Galley Image", + fixed=False, + check_for_existing_images=False, ): filename = uploaded_file.name # Check if an image with this name already exists for this galley. @@ -197,8 +211,8 @@ def save_galley_image( messages.add_message( request, messages.WARNING, - f'An image called {filename} already exists. Use the ' - f'replace file function to upload a new version.', + f"An image called {filename} already exists. Use the " + f"replace file function to upload a new version.", ) return else: @@ -209,13 +223,13 @@ def save_galley_image( replacement_file = files.overwrite_file( uploaded_file, image_to_overwrite, - ('articles', galley.article.pk), + ("articles", galley.article.pk), ) messages.add_message( request, messages.INFO, - f'An image called {filename} already exists. It has been ' - f'overwritten with your uploaded file.' + f"An image called {filename} already exists. It has been " + f"overwritten with your uploaded file.", ) return replacement_file else: @@ -238,8 +252,8 @@ def save_galley_image( messages.add_message( request, messages.WARNING, - f'The file you uploaded does not have the expected ' - f'type: {new_file_mime}.' + f"The file you uploaded does not have the expected " + f"type: {new_file_mime}.", ) new_file = files.save_file_to_article(uploaded_file, galley.article, request.user) @@ -256,17 +270,19 @@ def save_galley_image( def use_data_file_as_galley_image(galley, request, label): - file_id = request.POST.get('datafile') + file_id = request.POST.get("datafile") if file_id: try: file = core_models.File.objects.get(pk=file_id) - file.original_filename = request.POST.get('file_name') + file.original_filename = request.POST.get("file_name") file.save() galley.images.add(file) - messages.add_message(request, messages.SUCCESS, 'File added.') + messages.add_message(request, messages.SUCCESS, "File added.") except core_models.File.DoesNotExist: - messages.add_message(request, messages.WARNING, 'No file with given ID found.') + messages.add_message( + request, messages.WARNING, "No file with given ID found." + ) def save_galley_css(galley, request, uploaded_file, filename, label="Galley Image"): @@ -294,18 +310,22 @@ def get_copyedit_files(article): def handle_self_typesetter_assignment(production_assignment, request): - user = get_object_or_404(core_models.Account, pk=request.POST.get('typesetter_id')) + user = get_object_or_404(core_models.Account, pk=request.POST.get("typesetter_id")) typeset_task = models.TypesetTask( assignment=production_assignment, typesetter=user, notified=True, accepted=timezone.now(), - typeset_task='This is a self assignment.', + typeset_task="This is a self assignment.", ) typeset_task.save() - messages.add_message(request, messages.SUCCESS, 'You have been assigned as a typesetter to this article.') + messages.add_message( + request, + messages.SUCCESS, + "You have been assigned as a typesetter to this article.", + ) return typeset_task @@ -334,8 +354,7 @@ def typesetter_users(typesetters): def update_typesetter_task(typeset, request): - - file_ints = request.POST.getlist('files', []) + file_ints = request.POST.getlist("files", []) files = [core_models.File.objects.get(pk=f) for f in file_ints] for file in files: @@ -345,24 +364,28 @@ def update_typesetter_task(typeset, request): if file not in files: typeset.files_for_typesetting.remove(file) - typeset.typeset_task = request.POST.get('typeset_task', '') + typeset.typeset_task = request.POST.get("typeset_task", "") typeset.save() def get_typesetter_notification(typeset_task, request): context = { - 'typeset_task': typeset_task, - 'typesetter_requests_url': request.journal.site_url(path=reverse('typesetter_requests')), + "typeset_task": typeset_task, + "typesetter_requests_url": request.journal.site_url( + path=reverse("typesetter_requests") + ), } - return render_template.get_message_content(request, context, 'typesetter_notification') + return render_template.get_message_content( + request, context, "typesetter_notification" + ) def get_complete_template(request, article, production_assignment): context = { - 'article': article, - 'production_assignment': production_assignment, + "article": article, + "production_assignment": production_assignment, } - return render_template.get_message_content(request, context, 'production_complete') + return render_template.get_message_content(request, context, "production_complete") def get_image_names(galley): @@ -373,13 +396,13 @@ def handle_delete_request(request, galley, typeset_task=None, article=None, page if typeset_task: article = typeset_task.assignment.article - file_id = request.POST.get('delete', None) + file_id = request.POST.get("delete", None) if file_id: try: file_to_delete = core_models.File.objects.get(pk=file_id) if file_to_delete.pk in galley.article.all_galley_file_pks(): - messages.add_message(request, messages.INFO, 'File deleted') + messages.add_message(request, messages.INFO, "File deleted") # Check if this is a data file, and if it is remove it, # dont delete it. @@ -388,44 +411,59 @@ def handle_delete_request(request, galley, typeset_task=None, article=None, page else: file_to_delete.delete() except core_models.File.DoesNotExist: - messages.add_message(request, messages.WARNING, 'File not found') - - if page == 'edit': - return redirect(reverse('edit_galley', kwargs={'typeset_id': typeset_task.pk, 'galley_id': galley.pk})) - elif page == 'pm_edit': - return redirect(reverse('production_article', kwargs={'article_id': article.pk})) - elif page == 'typeset': - return redirect(reverse('do_typeset_task', kwargs={'typeset_id': typeset_task.pk})) + messages.add_message(request, messages.WARNING, "File not found") + + if page == "edit": + return redirect( + reverse( + "edit_galley", + kwargs={"typeset_id": typeset_task.pk, "galley_id": galley.pk}, + ) + ) + elif page == "pm_edit": + return redirect( + reverse("production_article", kwargs={"article_id": article.pk}) + ) + elif page == "typeset": + return redirect( + reverse("do_typeset_task", kwargs={"typeset_id": typeset_task.pk}) + ) else: - return redirect(reverse('assigned_article', kwargs={'article_id': article.pk})) + return redirect(reverse("assigned_article", kwargs={"article_id": article.pk})) def get_production_assign_content(user, request, article, url): context = { - 'user': user, - 'url': url, - 'article': article, + "user": user, + "url": url, + "article": article, } - return render_template.get_message_content(request, context, 'production_assign_article') + return render_template.get_message_content( + request, context, "production_assign_article" + ) def edit_galley_redirect(typeset_task, galley, return_url, article): if typeset_task: return redirect( reverse( - 'edit_galley', - kwargs={'typeset_id': typeset_task.pk, 'galley_id': galley.pk}, + "edit_galley", + kwargs={"typeset_id": typeset_task.pk, "galley_id": galley.pk}, ) ) else: - return_path = '?return={return_url}'.format( - return_url=return_url, - ) if return_url else '' + return_path = ( + "?return={return_url}".format( + return_url=return_url, + ) + if return_url + else "" + ) url = reverse( - 'pm_edit_galley', - kwargs={'article_id': article.pk, 'galley_id': galley.pk}, + "pm_edit_galley", + kwargs={"article_id": article.pk, "galley_id": galley.pk}, ) - redirect_url = '{url}{return_path}'.format( + redirect_url = "{url}{return_path}".format( url=url, return_path=return_path, ) @@ -436,28 +474,24 @@ def zip_redirect(typeset_id, article_id, galley_id): if typeset_id: return redirect( reverse( - 'typesetter_zip_uploader', + "typesetter_zip_uploader", kwargs={ - 'typeset_id': typeset_id, - 'galley_id': galley_id, - } + "typeset_id": typeset_id, + "galley_id": galley_id, + }, ) ) else: return redirect( reverse( - 'pm_zip_uploader', - kwargs={ - 'article_id': article_id, - 'galley_id': galley_id - } + "pm_zip_uploader", + kwargs={"article_id": article_id, "galley_id": galley_id}, ) ) def handle_zipped_galley_images(zip_file, galley, request): - - with zipfile.ZipFile(zip_file, 'r') as zf: + with zipfile.ZipFile(zip_file, "r") as zf: for finfo in zf.infolist(): zipped_file = zf.open(finfo) content_file = ContentFile(zipped_file.read()) @@ -482,9 +516,7 @@ def handle_zipped_galley_images(zip_file, galley, request): ) updated_file = files.overwrite_file( - content_file, - file, - ('articles', galley.article.pk) + content_file, file, ("articles", galley.article.pk) ) updated_file.original_filename = zipped_file.name updated_file.save() @@ -492,28 +524,24 @@ def handle_zipped_galley_images(zip_file, galley, request): messages.add_message( request, messages.SUCCESS, - 'New version of {} saved.'.format(zipped_file.name) + "New version of {} saved.".format(zipped_file.name), ) except core_models.File.DoesNotExist: messages.add_message( request, messages.ERROR, - 'A file was found in XML and Zip but no corresponding' - 'File object could be found. {}'.format( + "A file was found in XML and Zip but no corresponding" + "File object could be found. {}".format( zipped_file.name, - ) + ), ) - messages.add_message( - request, - messages.SUCCESS, - '' - ) + messages.add_message(request, messages.SUCCESS, "") else: messages.add_message( request, messages.WARNING, - 'File {} not found in XML'.format(zipped_file.name) + "File {} not found in XML".format(zipped_file.name), ) return diff --git a/src/production/migrations/0001_initial.py b/src/production/migrations/0001_initial.py index 29f54abf31..a3c28e9797 100755 --- a/src/production/migrations/0001_initial.py +++ b/src/production/migrations/0001_initial.py @@ -9,64 +9,128 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('core', '0001_initial'), - ('submission', '0001_initial'), + ("core", "0001_initial"), + ("submission", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='ProductionAssignment', + name="ProductionAssignment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('closed', models.DateField(blank=True, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("closed", models.DateField(blank=True, null=True)), ], ), migrations.CreateModel( - name='TypesetTask', + name="TypesetTask", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('accepted', models.DateTimeField(blank=True, null=True)), - ('typeset_task', models.TextField(blank=True, null=True, verbose_name='Typesetting Task')), - ('note_from_typesetter', models.TextField(blank=True, null=True, verbose_name='Note to Editor')), - ('completed', models.DateTimeField(blank=True, null=True)), - ('editor_reviewed', models.BooleanField(default=False)), - ('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='production.ProductionAssignment')), - ('files_for_typesetting', models.ManyToManyField(related_name='files_for_typesetting', to='core.File')), - ('galleys_loaded', models.ManyToManyField(related_name='galleys_loaded', to='core.File')), - ('typesetter', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("accepted", models.DateTimeField(blank=True, null=True)), + ( + "typeset_task", + models.TextField( + blank=True, null=True, verbose_name="Typesetting Task" + ), + ), + ( + "note_from_typesetter", + models.TextField( + blank=True, null=True, verbose_name="Note to Editor" + ), + ), + ("completed", models.DateTimeField(blank=True, null=True)), + ("editor_reviewed", models.BooleanField(default=False)), + ( + "assignment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="production.ProductionAssignment", + ), + ), + ( + "files_for_typesetting", + models.ManyToManyField( + related_name="files_for_typesetting", to="core.File" + ), + ), + ( + "galleys_loaded", + models.ManyToManyField( + related_name="galleys_loaded", to="core.File" + ), + ), + ( + "typesetter", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='productionassignment', - name='accepted_by_manager', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='production.TypesetTask'), + model_name="productionassignment", + name="accepted_by_manager", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="production.TypesetTask", + ), ), migrations.AddField( - model_name='productionassignment', - name='article', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="productionassignment", + name="article", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='productionassignment', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prod_editor', to=settings.AUTH_USER_MODEL), + model_name="productionassignment", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="prod_editor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='productionassignment', - name='production_manager', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="productionassignment", + name="production_manager", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterUniqueTogether( - name='productionassignment', - unique_together=set([('article', 'production_manager')]), + name="productionassignment", + unique_together=set([("article", "production_manager")]), ), ] diff --git a/src/production/migrations/0002_typesettask_due.py b/src/production/migrations/0002_typesettask_due.py index fb3648d5e2..692ae39658 100644 --- a/src/production/migrations/0002_typesettask_due.py +++ b/src/production/migrations/0002_typesettask_due.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('production', '0001_initial'), + ("production", "0001_initial"), ] operations = [ migrations.AddField( - model_name='typesettask', - name='due', + model_name="typesettask", + name="due", field=models.DateField(null=True), ), ] diff --git a/src/production/migrations/0003_auto_20200529_1415.py b/src/production/migrations/0003_auto_20200529_1415.py index 3a6511cfaa..9b1359de57 100644 --- a/src/production/migrations/0003_auto_20200529_1415.py +++ b/src/production/migrations/0003_auto_20200529_1415.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('production', '0002_typesettask_due'), + ("production", "0002_typesettask_due"), ] operations = [ migrations.AlterField( - model_name='typesettask', - name='galleys_loaded', - field=models.ManyToManyField(blank=True, related_name='galleys_loaded', to='core.File'), + model_name="typesettask", + name="galleys_loaded", + field=models.ManyToManyField( + blank=True, related_name="galleys_loaded", to="core.File" + ), ), ] diff --git a/src/production/migrations/0004_alter_productionassignment_accepted_by_manager.py b/src/production/migrations/0004_alter_productionassignment_accepted_by_manager.py index 5db5f52f5b..efb46356d6 100644 --- a/src/production/migrations/0004_alter_productionassignment_accepted_by_manager.py +++ b/src/production/migrations/0004_alter_productionassignment_accepted_by_manager.py @@ -5,15 +5,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('production', '0003_auto_20200529_1415'), + ("production", "0003_auto_20200529_1415"), ] operations = [ migrations.AlterField( - model_name='productionassignment', - name='accepted_by_manager', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='production.typesettask'), + model_name="productionassignment", + name="accepted_by_manager", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="production.typesettask", + ), ), ] diff --git a/src/production/models.py b/src/production/models.py index a5284356ba..c24f6f4318 100755 --- a/src/production/models.py +++ b/src/production/models.py @@ -12,20 +12,26 @@ class ProductionAssignment(models.Model): - article = models.OneToOneField('submission.Article', on_delete=models.CASCADE) - production_manager = models.ForeignKey('core.Account', null=True, on_delete=models.SET_NULL) - editor = models.ForeignKey('core.Account', null=True, on_delete=models.SET_NULL, related_name='prod_editor') + article = models.OneToOneField("submission.Article", on_delete=models.CASCADE) + production_manager = models.ForeignKey( + "core.Account", null=True, on_delete=models.SET_NULL + ) + editor = models.ForeignKey( + "core.Account", null=True, on_delete=models.SET_NULL, related_name="prod_editor" + ) assigned = models.DateTimeField(default=timezone.now) notified = models.BooleanField(default=False) closed = models.DateField(blank=True, null=True) - accepted_by_manager = models.ForeignKey('TypesetTask', null=True, blank=True, on_delete=models.SET_NULL) + accepted_by_manager = models.ForeignKey( + "TypesetTask", null=True, blank=True, on_delete=models.SET_NULL + ) class Meta: - unique_together = ('article', 'production_manager') + unique_together = ("article", "production_manager") def __str__(self): - return 'PM Assignment {pk}'.format(pk=self.pk) + return "PM Assignment {pk}".format(pk=self.pk) def typeset_tasks(self): return self.typesettask_set.all() @@ -39,8 +45,10 @@ def completed_typeset_tasks(self): class ActiveTypesetTaskManager(models.Manager): def get_queryset(self): - return super(ActiveTypesetTaskManager, self).get_queryset().exclude( - assignment__article__stage=submission_models.STAGE_ARCHIVED + return ( + super(ActiveTypesetTaskManager, self) + .get_queryset() + .exclude(assignment__article__stage=submission_models.STAGE_ARCHIVED) ) @@ -50,7 +58,7 @@ class TypesetTask(models.Model): on_delete=models.CASCADE, ) typesetter = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -65,18 +73,18 @@ class TypesetTask(models.Model): verbose_name="Typesetting Task", ) files_for_typesetting = models.ManyToManyField( - 'core.File', - related_name='files_for_typesetting', + "core.File", + related_name="files_for_typesetting", ) galleys_loaded = models.ManyToManyField( - 'core.File', + "core.File", blank=True, - related_name='galleys_loaded', + related_name="galleys_loaded", ) note_from_typesetter = models.TextField( blank=True, null=True, - verbose_name='Note to Editor', + verbose_name="Note to Editor", ) completed = models.DateTimeField(blank=True, null=True) @@ -115,13 +123,13 @@ def is_overdue(self): return False FRIENDLY_STATUSES = { - "assigned": "Awaiting response", - "accepted": "Task accepted", - "declined": "Task declined", - "completed": "Task completed", - "closed": "Task closed", - "unknown": "Task status unknown", - } + "assigned": "Awaiting response", + "accepted": "Task accepted", + "declined": "Task declined", + "completed": "Task completed", + "closed": "Task closed", + "unknown": "Task status unknown", + } @property def friendly_status(self): diff --git a/src/production/tests.py b/src/production/tests.py index 7db8ce6581..60a8ca1da5 100644 --- a/src/production/tests.py +++ b/src/production/tests.py @@ -9,21 +9,22 @@ class TestLogic(TestCase): - @classmethod def setUpTestData(cls): clear_script_prefix() cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() cls.editor = helpers.create_user( - 'typesetter@janeway.systems', - roles=['editor'], + "typesetter@janeway.systems", + roles=["editor"], journal=cls.journal_one, - atrrs={'is_active': True,} + atrrs={ + "is_active": True, + }, ) cls.typesetter = helpers.create_user( - 'typesetter@janeway.systems', - roles=['typesetter'], + "typesetter@janeway.systems", + roles=["typesetter"], journal=cls.journal_one, ) cls.typesetter.is_active = True @@ -44,7 +45,7 @@ def setUpTestData(cls): cls.active_typesetting_task = models.TypesetTask.objects.create( assignment=cls.active_production_assignment, typesetter=cls.typesetter, - typeset_task='Active Task', + typeset_task="Active Task", ) cls.archived_production_assignment = models.ProductionAssignment.objects.create( article=cls.archived_article, @@ -54,7 +55,7 @@ def setUpTestData(cls): cls.archived_typesetting_task = models.TypesetTask.objects.create( assignment=cls.active_production_assignment, typesetter=cls.typesetter, - typeset_task='Archive Task', + typeset_task="Archive Task", ) def test_remove_css_from_html(self): @@ -71,33 +72,26 @@ def test_remove_css_from_html(self): """ - expected_html = '\n\n\n\n\n\n\n

This is a paragraph.

\n\n\n' + expected_html = "\n\n\n\n\n\n\n

This is a paragraph.

\n\n\n" result = remove_css_from_html(test_html) self.assertMultiLineEqual(result, expected_html) def test_archive_stage_hides_task(self): self.client.force_login(self.typesetter) - response = self.client.get( - reverse('typesetter_requests') - ) + response = self.client.get(reverse("typesetter_requests")) self.assertContains( response, - 'Active Task', - ) - self.assertNotContains( - response, - 'Archived Task' + "Active Task", ) + self.assertNotContains(response, "Archived Task") def test_archived_article_task_404s(self): self.client.force_login(self.typesetter) response = self.client.get( reverse( - 'do_typeset_task', - kwargs={ - 'typeset_id': self.archived_typesetting_task.pk - } + "do_typeset_task", + kwargs={"typeset_id": self.archived_typesetting_task.pk}, ) ) self.assertTrue( @@ -109,15 +103,11 @@ def test_active_article_task_200s(self): self.client.force_login(self.typesetter) response = self.client.get( reverse( - 'do_typeset_task', - kwargs={ - 'typeset_id': self.archived_typesetting_task.pk - } + "do_typeset_task", + kwargs={"typeset_id": self.archived_typesetting_task.pk}, ) ) self.assertTrue( response.status_code, 200, ) - - diff --git a/src/production/urls.py b/src/production/urls.py index ab2a9bea5f..37e1539678 100755 --- a/src/production/urls.py +++ b/src/production/urls.py @@ -8,75 +8,105 @@ urlpatterns = [ # Editor - re_path(r'^$', - views.production_list, - name='production_list'), - re_path(r'^(?P\d+)/no_assignment/$', + re_path(r"^$", views.production_list, name="production_list"), + re_path( + r"^(?P\d+)/no_assignment/$", views.non_workflow_assign_article, - name='production_non_workflow_assign'), - + name="production_non_workflow_assign", + ), # Production Manager - re_path(r'^(?P\d+)/$', - views.production_article, - name='production_article'), re_path( - r'^(?P\d+)/preview/(?P\d+)/$', + r"^(?P\d+)/$", views.production_article, name="production_article" + ), + re_path( + r"^(?P\d+)/preview/(?P\d+)/$", views.preview_galley, - name='production_preview_galley' - ), - re_path(r'^(?P\d+)/preview/(?P\d+)/(?P.*)$', + name="production_preview_galley", + ), + re_path( + r"^(?P\d+)/preview/(?P\d+)/(?P.*)$", views.preview_figure, - name='production_preview_figure' - ), - re_path(r'^assign/(?P\d+)/user/(?P\d+)$', + name="production_preview_figure", + ), + re_path( + r"^assign/(?P\d+)/user/(?P\d+)$", views.production_assign_article, - name='production_assign_article'), - re_path(r'^unassign/(?P\d+)/$', + name="production_assign_article", + ), + re_path( + r"^unassign/(?P\d+)/$", views.production_unassign_article, - name='production_unassign_article'), - re_path(r'^(?P\d+)/galley/(?P\d+)/$', + name="production_unassign_article", + ), + re_path( + r"^(?P\d+)/galley/(?P\d+)/$", views.edit_galley, - name='pm_edit_galley'), - re_path(r'^(?P\d+)/galley/(?P\d+)/zip_uploader/$', + name="pm_edit_galley", + ), + re_path( + r"^(?P\d+)/galley/(?P\d+)/zip_uploader/$", views.upload_image_zip, - name='pm_zip_uploader'), - re_path(r'^(?P\d+)/task/(?P\d+)/reviewed/$', + name="pm_zip_uploader", + ), + re_path( + r"^(?P\d+)/task/(?P\d+)/reviewed/$", views.review_typeset_task, - name='review_typeset_task'), - re_path(r'^(?P\d+)/done/$', - views.production_done, name='production_done'), - + name="review_typeset_task", + ), + re_path( + r"^(?P\d+)/done/$", views.production_done, name="production_done" + ), # Typeset Assignment - re_path(r'^(?P\d+)/assignment/(?P\d+)/typeset/assign/$', - views.assign_typesetter, name='assign_typesetter'), - re_path(r'^typeset/(?P\d+)/notify/$', - views.notify_typesetter, name='notify_typesetter'), - re_path(r'^typeset/(?P\d+)/notify/event/(?Ptrue|false)$', - views.notify_typesetter, name='notify_typesetter_event'), - re_path(r'^typeset/(?P\d+)/delete/$', - views.edit_typesetter_assignment, name='edit_typesetter_assignment'), - - re_path(r'^(?P\d+)/supp_file/(?P\d+)/doi/$', + re_path( + r"^(?P\d+)/assignment/(?P\d+)/typeset/assign/$", + views.assign_typesetter, + name="assign_typesetter", + ), + re_path( + r"^typeset/(?P\d+)/notify/$", + views.notify_typesetter, + name="notify_typesetter", + ), + re_path( + r"^typeset/(?P\d+)/notify/event/(?Ptrue|false)$", + views.notify_typesetter, + name="notify_typesetter_event", + ), + re_path( + r"^typeset/(?P\d+)/delete/$", + views.edit_typesetter_assignment, + name="edit_typesetter_assignment", + ), + re_path( + r"^(?P\d+)/supp_file/(?P\d+)/doi/$", views.supp_file_doi, - name='supp_file_doi'), - + name="supp_file_doi", + ), # Typesetter - re_path(r'^requests/$', - views.typesetter_requests, - name='typesetter_requests'), - re_path(r'^requests/(?P\d+)/decision/(?Paccept|decline)/$', + re_path(r"^requests/$", views.typesetter_requests, name="typesetter_requests"), + re_path( + r"^requests/(?P\d+)/decision/(?Paccept|decline)/$", views.typesetter_requests, - name='typesetter_requests_decision'), - re_path(r'^requests/(?P\d+)/$', + name="typesetter_requests_decision", + ), + re_path( + r"^requests/(?P\d+)/$", views.do_typeset_task, - name='do_typeset_task'), - re_path(r'^requests/(?P\d+)/galley/(?P\d+)/$', + name="do_typeset_task", + ), + re_path( + r"^requests/(?P\d+)/galley/(?P\d+)/$", views.edit_galley, - name='edit_galley'), - re_path(r'^requests/(?P\d+)/galley/(?P\d+)/zip_uploader/$', + name="edit_galley", + ), + re_path( + r"^requests/(?P\d+)/galley/(?P\d+)/zip_uploader/$", views.upload_image_zip, - name='typesetter_zip_uploader'), - re_path(r'^requests/(?P\d+)/galley/(?P\d+)/delete/$', + name="typesetter_zip_uploader", + ), + re_path( + r"^requests/(?P\d+)/galley/(?P\d+)/delete/$", views.delete_galley, - name='delete_galley'), + name="delete_galley", + ), ] diff --git a/src/production/views.py b/src/production/views.py index 1f3131feee..d5593e25d9 100755 --- a/src/production/views.py +++ b/src/production/views.py @@ -28,7 +28,7 @@ typesetter_or_editor_required, typesetter_user_required, typesetting_user_or_production_user_or_editor_required, - production_manager_roles + production_manager_roles, ) from submission import models as submission_models from utils import setting_handler @@ -46,43 +46,31 @@ def production_list(request): article__journal=request.journal ) my_table = models.ProductionAssignment.objects.values_list( - 'article_id', - flat=True - ).filter( - production_manager=request.user, - article__journal=request.journal - ) + "article_id", flat=True + ).filter(production_manager=request.user, article__journal=request.journal) assigned = [assignment.article.pk for assignment in assigned_table] unassigned_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_TYPESETTING, - journal=request.journal - ).exclude( - id__in=assigned - ) + stage=submission_models.STAGE_TYPESETTING, journal=request.journal + ).exclude(id__in=assigned) assigned_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_TYPESETTING, - journal=request.journal - ).exclude( - id__in=unassigned_articles - ) + stage=submission_models.STAGE_TYPESETTING, journal=request.journal + ).exclude(id__in=unassigned_articles) my_articles = submission_models.Article.objects.filter( - stage=submission_models.STAGE_TYPESETTING, - id__in=my_table + stage=submission_models.STAGE_TYPESETTING, id__in=my_table ) prod_managers = core_models.AccountRole.objects.filter( - role__slug='production', - journal=request.journal + role__slug="production", journal=request.journal ) - template = 'production/index.html' + template = "production/index.html" context = { - 'production_articles': unassigned_articles, - 'assigned_articles': assigned_articles, - 'production_managers': prod_managers, - 'my_articles': my_articles, + "production_articles": unassigned_articles, + "assigned_articles": assigned_articles, + "production_managers": prod_managers, + "my_articles": my_articles, } return render(request, template, context) @@ -108,8 +96,8 @@ def production_assign_article(request, user_id, article_id): if user.is_production(request): url = request.journal.site_url( path=reverse( - 'production_article', - kwargs={'article_id': article.id}, + "production_article", + kwargs={"article_id": article.id}, ) ) html = logic.get_production_assign_content(user, request, article, url) @@ -123,7 +111,7 @@ def production_assign_article(request, user_id, article_id): cron_task.CronTask.add_email_task( user.email, - 'Production assignment', + "Production assignment", html, request, article, @@ -132,10 +120,10 @@ def production_assign_article(request, user_id, article_id): messages.add_message( request, messages.WARNING, - 'User is not a production manager.', + "User is not a production manager.", ) - return redirect(reverse('production_list')) + return redirect(reverse("production_list")) @editor_user_required @@ -154,7 +142,7 @@ def production_unassign_article(request, article_id): models.ProductionAssignment.objects.filter(article=article).delete() - return redirect(reverse('production_list')) + return redirect(reverse("production_list")) @editor_user_required @@ -176,17 +164,17 @@ def non_workflow_assign_article(request, article_id): messages.add_message( request, messages.WARNING, - 'This article already has a production assignment.', + "This article already has a production assignment.", ) return redirect( reverse( - 'production_article', - kwargs={'article_id': article.pk}, + "production_article", + kwargs={"article_id": article.pk}, ) ) - if request.POST and 'assign' in request.POST: + if request.POST and "assign" in request.POST: models.ProductionAssignment.objects.create( article=article, editor=request.user, @@ -198,19 +186,19 @@ def non_workflow_assign_article(request, article_id): messages.add_message( request, messages.SUCCESS, - 'Production Assignment created.', + "Production Assignment created.", ) return redirect( reverse( - 'production_article', - kwargs={'article_id': article.pk}, + "production_article", + kwargs={"article_id": article.pk}, ) ) - template = 'production/non_workflow_assign.html' + template = "production/non_workflow_assign.html" context = { - 'article': article, + "article": article, } return render(request, template, context) @@ -246,21 +234,28 @@ def production_done(request, article_id): assignment.save() kwargs = { - 'request': request, - 'article': article, - 'assignment': assignment, - 'user_content_message': request.POST.get('user_content_message'), - 'skip': True if 'skip' in request.POST else False + "request": request, + "article": article, + "assignment": assignment, + "user_content_message": request.POST.get("user_content_message"), + "skip": True if "skip" in request.POST else False, } event_logic.Events.raise_event(event_logic.Events.ON_PRODUCTION_COMPLETE, **kwargs) - if request.journal.element_in_workflow(element_name='production'): - workflow_kwargs = {'handshake_url': 'production_list', 'request': request, 'article': article, - 'switch_stage': True} - return event_logic.Events.raise_event(event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, task_object=article, - **workflow_kwargs) + if request.journal.element_in_workflow(element_name="production"): + workflow_kwargs = { + "handshake_url": "production_list", + "request": request, + "article": article, + "switch_stage": True, + } + return event_logic.Events.raise_event( + event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, + task_object=article, + **workflow_kwargs, + ) else: - return redirect('proofing_list') + return redirect("proofing_list") @production_user_or_editor_required @@ -279,26 +274,24 @@ def production_article(request, article_id): galley_form = forms.GalleyForm() try: - production_assignment = models.ProductionAssignment.objects.get( - article=article - ) + production_assignment = models.ProductionAssignment.objects.get(article=article) except models.ProductionAssignment.DoesNotExist: return redirect( - reverse( - 'production_non_workflow_assign', - kwargs={'article_id': article.pk}, - ) - ) + reverse( + "production_non_workflow_assign", + kwargs={"article_id": article.pk}, + ) + ) galleys = logic.get_all_galleys(production_assignment.article) if request.POST: try: galley_form = forms.GalleyForm(request.POST, request.FILES) - if 'file' in request.FILES and galley_form.is_valid(): - label = galley_form.cleaned_data.get('label') - public = galley_form.cleaned_data.get('public') - for uploaded_file in request.FILES.getlist('file'): + if "file" in request.FILES and galley_form.is_valid(): + label = galley_form.cleaned_data.get("label") + public = galley_form.cleaned_data.get("public") + for uploaded_file in request.FILES.getlist("file"): logic.save_galley( article, request, @@ -326,63 +319,58 @@ def production_article(request, article_id): "Galleys must be uploaded individually, not zipped", ) - if 'prod' in request.POST: - for uploaded_file in request.FILES.getlist('prod-file'): + if "prod" in request.POST: + for uploaded_file in request.FILES.getlist("prod-file"): logic.save_prod_file( article, request, uploaded_file, - 'Production Ready File', + "Production Ready File", ) - if 'supp' in request.POST: - label = request.POST.get('label', 'Supplementary File') - for uploaded_file in request.FILES.getlist('supp-file'): + if "supp" in request.POST: + label = request.POST.get("label", "Supplementary File") + for uploaded_file in request.FILES.getlist("supp-file"): logic.save_supp_file(article, request, uploaded_file, label) - if 'source' in request.POST: - for uploaded_file in request.FILES.getlist('source-file'): + if "source" in request.POST: + for uploaded_file in request.FILES.getlist("source-file"): logic.save_source_file( article, request, uploaded_file, ) if not request.FILES: - messages.add_message( - request, - messages.WARNING, - 'No files uploaded.' - ) + messages.add_message(request, messages.WARNING, "No files uploaded.") if not galley_form.is_valid(): messages.add_message( request, messages.WARNING, - 'Galley form not valid.', + "Galley form not valid.", ) return redirect( - reverse( - 'production_article', - kwargs={'article_id': article.pk} - ) + reverse("production_article", kwargs={"article_id": article.pk}) ) manuscripts = article.manuscript_files.filter(is_galley=False) data_files = article.data_figure_files.filter(is_galley=False) copyedit_files = logic.get_copyedit_files(article) - template = 'production/assigned_article.html' + template = "production/assigned_article.html" context = { - 'article': article, - 'manuscripts': manuscripts, - 'data_files': data_files, - 'production_assignment': production_assignment, - 'copyedit_files': copyedit_files, - 'typeset_tasks': production_assignment.typesettask_set.all().order_by('-id'), - 'galleys': galleys, - 'complete_message': logic.get_complete_template(request, article, production_assignment), - 'galley_form': galley_form, + "article": article, + "manuscripts": manuscripts, + "data_files": data_files, + "production_assignment": production_assignment, + "copyedit_files": copyedit_files, + "typeset_tasks": production_assignment.typesettask_set.all().order_by("-id"), + "galleys": galleys, + "complete_message": logic.get_complete_template( + request, article, production_assignment + ), + "galley_form": galley_form, } return render(request, template, context) @@ -409,26 +397,26 @@ def preview_galley(request, article_id, galley_id): ) article_content = "" - if galley.type == 'xml' or galley.type == 'html': - template = 'proofing/preview/rendered.html' + if galley.type == "xml" or galley.type == "html": + template = "proofing/preview/rendered.html" try: article_content = galley.file_content() except Exception as e: messages.add_message( request, messages.ERROR, - 'Errors found rendering this galley', + "Errors found rendering this galley", ) - article_content = "Errors found rendering this galley: \n%s" % e - elif galley.type == 'epub': - template = 'proofing/preview/epub.html' + article_content = "Errors found rendering this galley: \n%s" % e + elif galley.type == "epub": + template = "proofing/preview/epub.html" else: - template = 'proofing/preview/embedded.html' + template = "proofing/preview/embedded.html" context = { - 'galley': galley, - 'article': article, - 'article_content': article_content, + "galley": galley, + "article": article, + "article_content": article_content, } return render(request, template, context) @@ -436,10 +424,12 @@ def preview_galley(request, article_id, galley_id): @typesetting_user_or_production_user_or_editor_required def preview_figure(request, article_id, galley_id, file_name): - galley = get_object_or_404(core_models.Galley, - pk=galley_id, - article__pk=article_id, - article__journal=request.journal) + galley = get_object_or_404( + core_models.Galley, + pk=galley_id, + article__pk=article_id, + article__journal=request.journal, + ) return article_figure(request, galley.article.pk, galley_id, file_name) @@ -473,17 +463,9 @@ def assign_typesetter(request, article_id, production_assignment_id): assignment=production_assignment, ) - if request.POST.get('typesetter_id'): - task = logic.handle_self_typesetter_assignment( - production_assignment, - request - ) - return redirect( - reverse( - 'do_typeset_task', - kwargs={'typeset_id': task.id} - ) - ) + if request.POST.get("typesetter_id"): + task = logic.handle_self_typesetter_assignment(production_assignment, request) + return redirect(reverse("do_typeset_task", kwargs={"typeset_id": task.id})) if request.POST: typesetter_form = forms.AssignTypesetter( @@ -499,19 +481,16 @@ def assign_typesetter(request, article_id, production_assignment_id): task = typesetter_form.save() return redirect( - reverse( - 'notify_typesetter', - kwargs={'typeset_id': task.pk} - ) + reverse("notify_typesetter", kwargs={"typeset_id": task.pk}) ) - template = 'production/assign_typesetter.html' + template = "production/assign_typesetter.html" context = { - 'production_assignment': production_assignment, - 'article': article, - 'copyedit_files': copyedit_files, - 'typesetters': typesetters, - 'form': typesetter_form, + "production_assignment": production_assignment, + "article": article, + "copyedit_files": copyedit_files, + "typesetters": typesetters, + "form": typesetter_form, } return render(request, template, context) @@ -537,42 +516,47 @@ def notify_typesetter(request, typeset_id, event=True): messages.add_message( request, messages.INFO, - 'A notification has already been sent for this task.', + "A notification has already been sent for this task.", ) return redirect( reverse( - 'production_article', - kwargs={'article_id': typeset.assignment.article.pk}, + "production_article", + kwargs={"article_id": typeset.assignment.article.pk}, ) ) user_message_content = logic.get_typesetter_notification(typeset, request) if request.POST: - user_message_content = request.POST.get('user_message_content') + user_message_content = request.POST.get("user_message_content") kwargs = { - 'user_message_content': user_message_content, - 'typeset_task': typeset, - 'request': request, - 'skip': True if 'skip' in request.POST else False, + "user_message_content": user_message_content, + "typeset_task": typeset, + "request": request, + "skip": True if "skip" in request.POST else False, } - if 'skip' not in request.POST: + if "skip" not in request.POST: typeset.notified = True typeset.save() - if event or event == 'true': + if event or event == "true": event_logic.Events.raise_event( - event_logic.Events.ON_TYPESET_TASK_ASSIGNED, **kwargs) + event_logic.Events.ON_TYPESET_TASK_ASSIGNED, **kwargs + ) - return redirect(reverse('production_article', kwargs={ - 'article_id': typeset.assignment.article.pk})) + return redirect( + reverse( + "production_article", + kwargs={"article_id": typeset.assignment.article.pk}, + ) + ) - template = 'production/notify_typesetter.html' + template = "production/notify_typesetter.html" context = { - 'typeset_task': typeset, - 'user_message_content': user_message_content, + "typeset_task": typeset, + "user_message_content": user_message_content, } return render(request, template, context) @@ -595,48 +579,43 @@ def edit_typesetter_assignment(request, typeset_id): article = typeset.assignment.article if request.POST: - if 'delete' in request.POST: + if "delete" in request.POST: messages.add_message( request, messages.SUCCESS, - 'Typeset task {0} has been deleted'.format(typeset.pk) + "Typeset task {0} has been deleted".format(typeset.pk), ) - kwargs = {'typeset': typeset, 'request': request} + kwargs = {"typeset": typeset, "request": request} event_logic.Events.raise_event( - event_logic.Events.ON_TYPESET_TASK_DELETED, - **kwargs + event_logic.Events.ON_TYPESET_TASK_DELETED, **kwargs ) typeset.delete() - elif 'update' in request.POST and typeset.accepted: + elif "update" in request.POST and typeset.accepted: messages.add_message( request, messages.WARNING, - 'This assignment has been accepted so cannot be edited.' + "This assignment has been accepted so cannot be edited.", ) - elif 'update' in request.POST: + elif "update" in request.POST: logic.update_typesetter_task(typeset, request) - elif 'reset' in request.POST and typeset.status == 'declined': + elif "reset" in request.POST and typeset.status == "declined": typeset.reset_task_dates() else: messages.add_message( request, messages.WARNING, - '[{status}] An invalid operation has ' - 'been attempted for this task.'.format( - status=typeset.friendly_status) + "[{status}] An invalid operation has " + "been attempted for this task.".format(status=typeset.friendly_status), ) return redirect( - reverse( - 'production_article', - kwargs={'article_id': article.pk} - ) + reverse("production_article", kwargs={"article_id": article.pk}) ) - template = 'production/edit_typesetter_assignment.html' + template = "production/edit_typesetter_assignment.html" context = { - 'typeset': typeset, - 'article': article, + "typeset": typeset, + "article": article, } return render(request, template, context) @@ -659,17 +638,23 @@ def typesetter_requests(request, typeset_id=None, decision=None): assignment__article__journal=request.journal, ) - if decision == 'accept': + if decision == "accept": typeset_task.accepted = timezone.now() - elif decision == 'decline': + elif decision == "decline": typeset_task.accepted = None typeset_task.completed = timezone.now() typeset_task.save() - kwargs = {'decision': decision, 'typeset_task': typeset_task, 'request': request} - event_logic.Events.raise_event(event_logic.Events.ON_TYPESETTER_DECISION, **kwargs) - return redirect(reverse('typesetter_requests')) + kwargs = { + "decision": decision, + "typeset_task": typeset_task, + "request": request, + } + event_logic.Events.raise_event( + event_logic.Events.ON_TYPESETTER_DECISION, **kwargs + ) + return redirect(reverse("typesetter_requests")) typeset_tasks = models.TypesetTask.active_objects.filter( accepted__isnull=True, @@ -692,11 +677,11 @@ def typesetter_requests(request, typeset_id=None, decision=None): assignment__article__journal=request.journal, ) - template = 'production/typesetter_requests.html' + template = "production/typesetter_requests.html" context = { - 'typeset_tasks': typeset_tasks, - 'in_progress_tasks': in_progress_tasks, - 'completed_tasks': completed_tasks, + "typeset_tasks": typeset_tasks, + "in_progress_tasks": in_progress_tasks, + "completed_tasks": completed_tasks, } return render(request, template, context) @@ -724,8 +709,7 @@ def do_typeset_task(request, typeset_id): galley_form = forms.GalleyForm() if request.POST: - - if 'complete' in request.POST: + if "complete" in request.POST: form = forms.TypesetterNote(request.POST, instance=typeset_task) if form.is_valid(): task = form.save() @@ -733,8 +717,8 @@ def do_typeset_task(request, typeset_id): task.save() kwargs = { - 'typeset_task': typeset_task, - 'request': request, + "typeset_task": typeset_task, + "request": request, } event_logic.Events.raise_event( event_logic.Events.ON_TYPESET_COMPLETE, @@ -744,18 +728,18 @@ def do_typeset_task(request, typeset_id): messages.add_message( request, messages.INFO, - 'Typeset assignment complete.', + "Typeset assignment complete.", ) - return redirect(reverse('typesetter_requests')) + return redirect(reverse("typesetter_requests")) new_galley = None - if 'file' in request.FILES: + if "file" in request.FILES: try: galley_form = forms.GalleyForm(request.POST, request.FILES) - if 'file' in request.FILES and galley_form.is_valid(): - label = galley_form.cleaned_data.get('label') - public = galley_form.cleaned_data.get('public') - for uploaded_file in request.FILES.getlist('file'): + if "file" in request.FILES and galley_form.is_valid(): + label = galley_form.cleaned_data.get("label") + public = galley_form.cleaned_data.get("public") + for uploaded_file in request.FILES.getlist("file"): logic.save_galley( article, request, @@ -783,8 +767,8 @@ def do_typeset_task(request, typeset_id): "Galleys must be uploaded individually, not zipped", ) - if 'source' in request.POST: - for uploaded_file in request.FILES.getlist('source-file'): + if "source" in request.POST: + for uploaded_file in request.FILES.getlist("source-file"): logic.save_source_file( article, request, @@ -795,29 +779,27 @@ def do_typeset_task(request, typeset_id): typeset_task.galleys_loaded.add(new_galley.file) if not request.FILES: - messages.add_message( - request, - messages.WARNING, - 'No files uploaded.' - ) + messages.add_message(request, messages.WARNING, "No files uploaded.") - return redirect(reverse('do_typeset_task', kwargs={'typeset_id': typeset_task.pk})) + return redirect( + reverse("do_typeset_task", kwargs={"typeset_id": typeset_task.pk}) + ) manuscripts = article.manuscript_files.filter(is_galley=False) data_files = article.data_figure_files.filter(is_galley=False) copyedit_files = logic.get_copyedit_files(article) - template = 'production/typeset_task.html' + template = "production/typeset_task.html" context = { - 'typeset_task': typeset_task, - 'article': article, - 'manuscripts': manuscripts, - 'data_files': data_files, - 'production_assignment': typeset_task.assignment, - 'copyedit_files': copyedit_files, - 'galleys': galleys, - 'form': form, - 'galley_form': galley_form, + "typeset_task": typeset_task, + "article": article, + "manuscripts": manuscripts, + "data_files": data_files, + "production_assignment": typeset_task.assignment, + "copyedit_files": copyedit_files, + "galleys": galleys, + "form": form, + "galley_form": galley_form, } return render(request, template, context) @@ -833,7 +815,7 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): :param article_id: Article PK, optional :return: HttpRedirect or HttpResponse """ - return_url = request.GET.get('return', None) + return_url = request.GET.get("return", None) if typeset_id: typeset_task = get_object_or_404( @@ -847,9 +829,7 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): else: typeset_task = None article = get_object_or_404( - submission_models.Article, - pk=article_id, - journal=request.journal + submission_models.Article, pk=article_id, journal=request.journal ) galley = get_object_or_404( @@ -861,16 +841,15 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): instance=galley, include_file=False, ) - if galley.label == 'XML': + if galley.label == "XML": xsl_files = core_models.XSLFile.objects.filter( - Q(journal=request.journal)|Q(journal__isnull=True) + Q(journal=request.journal) | Q(journal__isnull=True) ) else: xsl_files = None if request.POST: - - if 'delete' in request.POST: + if "delete" in request.POST: if typeset_task: logic.handle_delete_request( request, @@ -880,8 +859,8 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): ) return redirect( reverse( - 'do_typeset_task', - kwargs={'typeset_id': typeset_task.pk}, + "do_typeset_task", + kwargs={"typeset_id": typeset_task.pk}, ) ) else: @@ -894,23 +873,23 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): if not return_url: return redirect( reverse( - 'production_article', - kwargs={'article_id': article.pk}, + "production_article", + kwargs={"article_id": article.pk}, ) ) else: return redirect(return_url) - label = request.POST.get('label') + label = request.POST.get("label") - if 'fixed-image-upload' in request.POST: - if request.POST.get('datafile') is not None: + if "fixed-image-upload" in request.POST: + if request.POST.get("datafile") is not None: logic.use_data_file_as_galley_image( galley, request, label, ) - for uploaded_file in request.FILES.getlist('image'): + for uploaded_file in request.FILES.getlist("image"): logic.save_galley_image( galley, request, @@ -920,8 +899,8 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): check_for_existing_images=True, ) - if 'image-upload' in request.POST: - for uploaded_file in request.FILES.getlist('image'): + if "image-upload" in request.POST: + for uploaded_file in request.FILES.getlist("image"): logic.save_galley_image( galley, request, @@ -931,17 +910,17 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): check_for_existing_images=True, ) - elif 'css-upload' in request.POST: - for uploaded_file in request.FILES.getlist('css'): + elif "css-upload" in request.POST: + for uploaded_file in request.FILES.getlist("css"): logic.save_galley_css( galley, request, uploaded_file, - 'galley-{0}.css'.format(galley.id), + "galley-{0}.css".format(galley.id), label, ) - if 'galley-update' in request.POST: + if "galley-update" in request.POST: galley_form = forms.GalleyForm( request.POST, instance=galley, @@ -950,51 +929,57 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): if galley_form.is_valid(): galley_form.save() - if 'replace-galley' in request.POST: + if "replace-galley" in request.POST: logic.replace_galley_file( - article, request, + article, + request, galley, - request.FILES.get('galley'), + request.FILES.get("galley"), ) - if 'xsl_file' in request.POST: - xsl_file = get_object_or_404(core_models.XSLFile, - pk=request.POST["xsl_file"]) + if "xsl_file" in request.POST: + xsl_file = get_object_or_404( + core_models.XSLFile, pk=request.POST["xsl_file"] + ) galley.xsl_file = xsl_file galley.save() if typeset_task: return redirect( reverse( - 'edit_galley', - kwargs={'typeset_id': typeset_id, 'galley_id': galley_id}, + "edit_galley", + kwargs={"typeset_id": typeset_id, "galley_id": galley_id}, ) ) else: - return_path = '?return={return_url}'.format( - return_url=return_url, - ) if return_url else '' + return_path = ( + "?return={return_url}".format( + return_url=return_url, + ) + if return_url + else "" + ) url = reverse( - 'pm_edit_galley', - kwargs={'article_id': article.pk, 'galley_id': galley_id}, + "pm_edit_galley", + kwargs={"article_id": article.pk, "galley_id": galley_id}, ) - redirect_url = '{url}{return_path}'.format( + redirect_url = "{url}{return_path}".format( url=url, return_path=return_path, ) return redirect(redirect_url) - template = 'production/edit_galley.html' + template = "production/edit_galley.html" context = { - 'typeset_task': typeset_task, - 'galley': galley, - 'article': galley.article, - 'image_names': logic.get_image_names(galley), - 'return_url': return_url, - 'data_files': article.data_figure_files.all(), - 'galley_images': galley.images.all(), - 'xsl_files': xsl_files, - 'galley_form': galley_form, + "typeset_task": typeset_task, + "galley": galley, + "article": galley.article, + "image_names": logic.get_image_names(galley), + "return_url": return_url, + "data_files": article.data_figure_files.all(), + "galley_images": galley.images.all(), + "xsl_files": xsl_files, + "galley_form": galley_form, } return render(request, template, context) @@ -1002,7 +987,7 @@ def edit_galley(request, galley_id, typeset_id=None, article_id=None): @typesetter_or_editor_required def upload_image_zip(request, galley_id, typeset_id=None, article_id=None): - return_url = request.GET.get('return', None) + return_url = request.GET.get("return", None) if typeset_id: typeset_task = get_object_or_404( @@ -1016,9 +1001,7 @@ def upload_image_zip(request, galley_id, typeset_id=None, article_id=None): else: typeset_task = None article = get_object_or_404( - submission_models.Article, - pk=article_id, - journal=request.journal + submission_models.Article, pk=article_id, journal=request.journal ) galley = get_object_or_404( @@ -1027,8 +1010,8 @@ def upload_image_zip(request, galley_id, typeset_id=None, article_id=None): article=article, ) - if request.POST and 'zip_file' in request.POST: - file = request.FILES.get('file') + if request.POST and "zip_file" in request.POST: + file = request.FILES.get("file") try: logic.handle_zipped_galley_images(file, galley, request) return logic.edit_galley_redirect( @@ -1038,21 +1021,17 @@ def upload_image_zip(request, galley_id, typeset_id=None, article_id=None): article, ) except BadZipFile: - messages.add_message( - request, - messages.ERROR, - 'File must be a .zip file.' - ) + messages.add_message(request, messages.ERROR, "File must be a .zip file.") return logic.zip_redirect(typeset_id, article_id, galley_id) - template = 'production/upload_image_zip.html' + template = "production/upload_image_zip.html" context = { - 'typeset_task': typeset_task, - 'galley': galley, - 'article': galley.article, - 'galley_images': galley.images.all(), - 'image_names': logic.get_image_names(galley), - 'return_url': return_url, + "typeset_task": typeset_task, + "galley": galley, + "article": galley.article, + "galley_images": galley.images.all(), + "image_names": logic.get_image_names(galley), + "return_url": return_url, } return render(request, template, context) @@ -1077,7 +1056,7 @@ def review_typeset_task(request, article_id, typeset_id): typeset_task.editor_reviewed = True typeset_task.save() - return redirect(reverse('production_article', kwargs={'article_id': article.pk})) + return redirect(reverse("production_article", kwargs={"article_id": article.pk})) @typesetter_or_editor_required @@ -1097,7 +1076,7 @@ def delete_galley(request, typeset_id, galley_id): galley.file.unlink_file() galley.delete() - return redirect(reverse('do_typeset_task', kwargs={'typeset_id': typeset_id})) + return redirect(reverse("do_typeset_task", kwargs={"typeset_id": typeset_id})) @production_user_or_editor_required @@ -1119,66 +1098,62 @@ def supp_file_doi(request, article_id, supp_file_id): pk=supp_file_id, ) timestamp_suffix = article.journal.get_setting( - 'crossref', - 'crossref_date_suffix', + "crossref", + "crossref_date_suffix", ) test_mode = setting_handler.get_setting( - 'Identifiers', - 'crossref_test', - article.journal + "Identifiers", "crossref_test", article.journal ).processed_value if not article.get_doi(): messages.add_message( request, messages.INFO, - 'Parent article must have a DOI before you can assign a ' - 'supplementary file a DOI.') + "Parent article must have a DOI before you can assign a " + "supplementary file a DOI.", + ) xml_context = { - 'supp_file': supplementary_file, - 'article': article, - 'batch_id': uuid.uuid4(), - 'timestamp_suffix': timestamp_suffix, - 'depositor_name': setting_handler.get_setting( - 'Identifiers', - 'crossref_name', - article.journal + "supp_file": supplementary_file, + "article": article, + "batch_id": uuid.uuid4(), + "timestamp_suffix": timestamp_suffix, + "depositor_name": setting_handler.get_setting( + "Identifiers", "crossref_name", article.journal ).processed_value, - 'depositor_email': setting_handler.get_setting( - 'Identifiers', - 'crossref_email', - article.journal + "depositor_email": setting_handler.get_setting( + "Identifiers", "crossref_email", article.journal ).processed_value, - 'registrant': setting_handler.get_setting( - 'Identifiers', - 'crossref_registrant', - article.journal + "registrant": setting_handler.get_setting( + "Identifiers", "crossref_registrant", article.journal ).processed_value, - 'parent_doi': article.get_doi(), - 'now': datetime.datetime.now(), + "parent_doi": article.get_doi(), + "now": datetime.datetime.now(), } xml_content = render_to_string( - 'common/identifiers/crossref_component.xml', - xml_context, - request + "common/identifiers/crossref_component.xml", xml_context, request ) if request.POST: from identifiers import logic + logic.register_crossref_component(article, xml_content, supplementary_file) - supplementary_file.doi = '{0}.{1}'.format(article.get_doi(), supplementary_file.pk) + supplementary_file.doi = "{0}.{1}".format( + article.get_doi(), supplementary_file.pk + ) supplementary_file.save() - return redirect(reverse('production_article', kwargs={'article_id': article.pk})) + return redirect( + reverse("production_article", kwargs={"article_id": article.pk}) + ) - template = 'production/supp_file_doi.html' + template = "production/supp_file_doi.html" context = { - 'article': article, - 'supp_file': supplementary_file, - 'xml_content': xml_content, - 'test_mode': test_mode, + "article": article, + "supp_file": supplementary_file, + "xml_content": xml_content, + "test_mode": test_mode, } return render(request, template, context) diff --git a/src/proofing/admin.py b/src/proofing/admin.py index ec8eb7b171..e8ed84bbbb 100755 --- a/src/proofing/admin.py +++ b/src/proofing/admin.py @@ -8,36 +8,60 @@ class ProofingAssignmentAdmin(admin.ModelAdmin): - list_display = ('pk', 'article', 'proofing_manager', 'editor', 'assigned', 'completed') - list_filter = ('proofing_manager', 'editor') - raw_id_fields = ('article', 'proofing_manager', 'editor') + list_display = ( + "pk", + "article", + "proofing_manager", + "editor", + "assigned", + "completed", + ) + list_filter = ("proofing_manager", "editor") + raw_id_fields = ("article", "proofing_manager", "editor") class ProofingRound(admin.ModelAdmin): - list_display = ('pk', 'assignment', 'number', 'date_started') - list_filter = ('assignment',) - raw_id_fields = ('assignment',) + list_display = ("pk", "assignment", "number", "date_started") + list_filter = ("assignment",) + raw_id_fields = ("assignment",) class ProofingTaskAdmin(admin.ModelAdmin): - list_display = ('pk', 'round', 'proofreader', 'assigned', 'due', 'accepted', 'completed', 'cancelled') - list_filter = ('round', 'proofreader') - raw_id_fields = ('round', 'proofreader') - filter_horizontal = ('galleys_for_proofing', 'notes') + list_display = ( + "pk", + "round", + "proofreader", + "assigned", + "due", + "accepted", + "completed", + "cancelled", + ) + list_filter = ("round", "proofreader") + raw_id_fields = ("round", "proofreader") + filter_horizontal = ("galleys_for_proofing", "notes") class CorrectionTaskAdmin(admin.ModelAdmin): - list_display = ('pk', 'proofing_task', 'typesetter', 'assigned', 'due', 'accepted', 'completed') - list_filter = ('proofing_task', 'typesetter') - raw_id_fields = ('proofing_task', 'typesetter') - filter_horizontal = ('galleys',) + list_display = ( + "pk", + "proofing_task", + "typesetter", + "assigned", + "due", + "accepted", + "completed", + ) + list_filter = ("proofing_task", "typesetter") + raw_id_fields = ("proofing_task", "typesetter") + filter_horizontal = ("galleys",) class NoteAdmin(admin.ModelAdmin): - list_display = ('pk', 'galley', 'creator', 'date_time') - list_filter = ('galley', 'creator') - search_fields = ('text',) - raw_id_fields = ('galley', 'creator') + list_display = ("pk", "galley", "creator", "date_time") + list_filter = ("galley", "creator") + search_fields = ("text",) + raw_id_fields = ("galley", "creator") admin_list = [ diff --git a/src/proofing/forms.py b/src/proofing/forms.py index 5b8d65a7c6..100aed543a 100755 --- a/src/proofing/forms.py +++ b/src/proofing/forms.py @@ -10,8 +10,8 @@ class AssignProofreader(forms.ModelForm): class Meta: model = models.ProofingTask - fields = ('due', 'task') - widgets = {'galleys_for_proofing': forms.CheckboxSelectMultiple} + fields = ("due", "task") + widgets = {"galleys_for_proofing": forms.CheckboxSelectMultiple} class AssignTypesetter(forms.ModelForm): @@ -19,23 +19,23 @@ class AssignTypesetter(forms.ModelForm): class Meta: model = models.TypesetterProofingTask - fields = ('due', 'task') + fields = ("due", "task") def __init__(self, *args, **kwargs): - comments = kwargs.pop('comments', None) + comments = kwargs.pop("comments", None) super(AssignTypesetter, self).__init__(*args, **kwargs) - self.fields['due'].required = True + self.fields["due"].required = True if comments: - self.fields['task'].initial = comments + self.fields["task"].initial = comments def save(self, proofing_task, user, comments, commit=True): typeset_task = super(AssignTypesetter, self).save(commit=False) typeset_task.typesetter = user typeset_task.proofing_task = proofing_task - if self.cleaned_data['include_comments']: - typeset_task.task = '{0}
{1}'.format( + if self.cleaned_data["include_comments"]: + typeset_task.task = "{0}
{1}".format( typeset_task.task, comments, ) @@ -49,9 +49,9 @@ def save(self, proofing_task, user, comments, commit=True): class CompleteCorrections(forms.ModelForm): class Meta: model = models.TypesetterProofingTask - fields = ('notes',) + fields = ("notes",) def __init__(self, *args, **kwargs): super(CompleteCorrections, self).__init__(*args, **kwargs) - self.fields['notes'].required = True + self.fields["notes"].required = True diff --git a/src/proofing/logic.py b/src/proofing/logic.py index 11e32b25bf..5d2cb0581b 100755 --- a/src/proofing/logic.py +++ b/src/proofing/logic.py @@ -18,42 +18,56 @@ def handle_closing_active_task(request, article): if article.proofingassignment.current_proofing_round().has_active_tasks: - for task in article.proofingassignment.current_proofing_round().proofingtask_set.all(): + for ( + task + ) in article.proofingassignment.current_proofing_round().proofingtask_set.all(): if not task.completed: task.completed = timezone.now() task.cancelled = True task.save() kwargs = { - 'article': article, - 'proofing_task': task, - 'request': request, - 'user_content_message': request.POST.get('note_to_proofreaders', None) + "article": article, + "proofing_task": task, + "request": request, + "user_content_message": request.POST.get( + "note_to_proofreaders", None + ), } - event_logic.Events.raise_event(event_logic.Events.ON_CANCEL_PROOFING_TASK, - task_object=article, - **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_CANCEL_PROOFING_TASK, + task_object=article, + **kwargs, + ) else: - messages.add_message(request, messages.INFO, 'There are no active tasks.') + messages.add_message(request, messages.INFO, "There are no active tasks.") def get_all_possible_proofers(journal, article): - active_proofreaders = article.proofingassignment.current_proofing_round().active_proofreaders + active_proofreaders = ( + article.proofingassignment.current_proofing_round().active_proofreaders + ) - proofers = core_models.AccountRole.objects.filter( - (Q(role__slug='editor') | Q(role__slug='proofreader')) & Q(journal=journal) - ).exclude(user__in=article.authors.all()).exclude(user__in=active_proofreaders) - all_possible_proofers = [{'user': role.user, 'role': role.role.slug} for role in proofers] + proofers = ( + core_models.AccountRole.objects.filter( + (Q(role__slug="editor") | Q(role__slug="proofreader")) & Q(journal=journal) + ) + .exclude(user__in=article.authors.all()) + .exclude(user__in=active_proofreaders) + ) + all_possible_proofers = [ + {"user": role.user, "role": role.role.slug} for role in proofers + ] for author in article.authors.all(): if author not in active_proofreaders: - all_possible_proofers.append({'user': author, 'role': 'author'}) + all_possible_proofers.append({"user": author, "role": "author"}) return all_possible_proofers def get_user_from_post(request, article=False, typesetter=False): - user_id = request.POST.get('proofreader') + user_id = request.POST.get("proofreader") if user_id: user = core_models.Account.objects.get(pk=user_id) @@ -64,8 +78,7 @@ def get_user_from_post(request, article=False, typesetter=False): # return None if we aren't checking for typesetting and the user isn't a proofreder elif not typesetter and not ( - user.is_proofreader(request) - or user.is_editor(request) + user.is_proofreader(request) or user.is_editor(request) ): return None @@ -79,13 +92,15 @@ def get_user_from_post(request, article=False, typesetter=False): def get_galleys_from_post(request): - galley_id_list = request.POST.getlist('galleys_for_proofing') + galley_id_list = request.POST.getlist("galleys_for_proofing") - return [core_models.Galley.objects.get(pk=galley_id) for galley_id in galley_id_list] + return [ + core_models.Galley.objects.get(pk=galley_id) for galley_id in galley_id_list + ] def get_files_from_post(request): - file_id_list = request.POST.getlist('files_for_proofing') + file_id_list = request.POST.getlist("files_for_proofing") return [core_models.File.objects.get(pk=file_id) for file_id in file_id_list] @@ -93,29 +108,35 @@ def get_files_from_post(request): def get_notify_proofreader(request, article, proofing_task): proofing_url = request.journal.site_url(reverse("proofing_requests")) context = { - 'proofing_requests_url': proofing_url, - 'article': article, - 'proofing_task': proofing_task, + "proofing_requests_url": proofing_url, + "article": article, + "proofing_task": proofing_task, } - return render_template.get_message_content(request, context, 'notify_proofreader_assignment') + return render_template.get_message_content( + request, context, "notify_proofreader_assignment" + ) def get_notify_typesetter(request, article, proofing_task, typeset_task): proofing_url = request.journal.site_url(reverse("proofing_correction_requests")) context = { - 'proofing_requests_url': proofing_url, - 'article': article, - 'proofing_task': proofing_task, - 'typesetter_proofing_task': typeset_task, + "proofing_requests_url": proofing_url, + "article": article, + "proofing_task": proofing_task, + "typesetter_proofing_task": typeset_task, } - return render_template.get_message_content(request, context, 'notify_typesetter_proofing_changes') + return render_template.get_message_content( + request, context, "notify_typesetter_proofing_changes" + ) def create_html_snippet(note, proofing_task, galley): - template = get_template('proofing/note_snip.html') - html_content = template.render({'note': note, 'proofing_task': proofing_task, 'galley': galley}) + template = get_template("proofing/note_snip.html") + html_content = template.render( + {"note": note, "proofing_task": proofing_task, "galley": galley} + ) return html_content @@ -171,19 +192,25 @@ def get_typesetting_tasks(request): def handle_proof_decision(request, proofing_task_id, decision): - proofing_task = get_object_or_404(models.ProofingTask, - proofreader=request.user, - pk=proofing_task_id) - if decision == 'accept': + proofing_task = get_object_or_404( + models.ProofingTask, proofreader=request.user, pk=proofing_task_id + ) + if decision == "accept": proofing_task.accepted = timezone.now() - elif decision == 'decline': + elif decision == "decline": proofing_task.completed = timezone.now() proofing_task.save() - kwargs = {'request': request, 'proofing_task': proofing_task, 'decision': decision} - event_logic.Events.raise_event(event_logic.Events.ON_PROOFREADER_TASK_DECISION, - task_object=proofing_task.round.assignment.article, - **kwargs) - messages.add_message(request, messages.INFO, 'Proofing Task {0} decision: {1}'.format(proofing_task_id, decision)) + kwargs = {"request": request, "proofing_task": proofing_task, "decision": decision} + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFREADER_TASK_DECISION, + task_object=proofing_task.round.assignment.article, + **kwargs, + ) + messages.add_message( + request, + messages.INFO, + "Proofing Task {0} decision: {1}".format(proofing_task_id, decision), + ) def handle_typeset_decision(request, typeset_task_id, decision): @@ -193,23 +220,29 @@ def handle_typeset_decision(request, typeset_task_id, decision): pk=typeset_task_id, proofing_task__round__assignment__article__journal=request.journal, ) - if decision == 'accept': + if decision == "accept": typeset_task.accepted = timezone.now() - elif decision == 'decline': + elif decision == "decline": typeset_task.completed = timezone.now() typeset_task.save() - kwargs = {'request': request, 'typeset_task': typeset_task, 'decision': decision} - event_logic.Events.raise_event(event_logic.Events.ON_PROOFING_TYPESET_DECISION, - task_object=typeset_task.proofing_task.round.assignment.article, - **kwargs) - messages.add_message(request, messages.INFO, 'Typeset Task {0} decision: {1}'.format(typeset_task_id, decision)) + kwargs = {"request": request, "typeset_task": typeset_task, "decision": decision} + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFING_TYPESET_DECISION, + task_object=typeset_task.proofing_task.round.assignment.article, + **kwargs, + ) + messages.add_message( + request, + messages.INFO, + "Typeset Task {0} decision: {1}".format(typeset_task_id, decision), + ) def get_model_and_object(model_name, model_pk): model = None - if model_name == 'proofing': + if model_name == "proofing": model = models.ProofingTask - elif model_name == 'correction': + elif model_name == "correction": model = models.TypesetterProofingTask if model: @@ -219,28 +252,32 @@ def get_model_and_object(model_name, model_pk): def get_ack_message(request, article, model_name, model_object): - if model_name == 'proofing': + if model_name == "proofing": user = model_object.proofreader - elif model_name == 'correction': + elif model_name == "correction": user = model_object.typesetter else: user = None context = { - 'article': article, - 'user': user, + "article": article, + "user": user, } - return render_template.get_message_content(request, context, 'thank_proofreaders_and_typesetters') + return render_template.get_message_content( + request, context, "thank_proofreaders_and_typesetters" + ) def get_complete_proofing_message(request, article): context = { - 'article': article, - 'proofing_assignment': article.proofingassignment, + "article": article, + "proofing_assignment": article.proofingassignment, } - return render_template.get_message_content(request, context, 'notify_editor_proofing_complete') + return render_template.get_message_content( + request, context, "notify_editor_proofing_complete" + ) def get_typesetters(article, proofing_task): @@ -251,23 +288,24 @@ def get_typesetters(article, proofing_task): typesetters = [task.typesetter.pk for task in correction_tasks] - return core_models.AccountRole.objects.filter(role__slug='typesetter', journal=article.journal).exclude( - user__pk__in=typesetters) + return core_models.AccountRole.objects.filter( + role__slug="typesetter", journal=article.journal + ).exclude(user__pk__in=typesetters) def handle_annotated_galley_upload(request, proofing_task, article): - uploaded_files = request.FILES.getlist('file') + uploaded_files = request.FILES.getlist("file") if uploaded_files: for file in uploaded_files: new_file = files.save_file_to_article(file, article, request.user) - new_file.label = 'Annotated Proof' + new_file.label = "Annotated Proof" new_file.save() proofing_task.proofed_files.add(new_file) - messages.add_message(request, messages.SUCCESS, 'Annotated file uploaded.') + messages.add_message(request, messages.SUCCESS, "Annotated file uploaded.") return None else: - return 'uploadbox' + return "uploadbox" def add_reset_log_entry(request, proofing_task, article): @@ -279,16 +317,16 @@ def add_reset_log_entry(request, proofing_task, article): :return: None """ - description = '{user} reset proofing task {id} for article {title}'.format( + description = "{user} reset proofing task {id} for article {title}".format( user=request.user, id=proofing_task.pk, title=article.title, ) utils_models.LogEntry.add_entry( - types='Proofing Task Reset', + types="Proofing Task Reset", description=description, - level='Info', + level="Info", actor=request.user, request=request, target=article, @@ -306,4 +344,3 @@ def delete_round(article, round): if not article.proofingassignment.proofinground_set.all(): article.proofingassignment.add_new_proofing_round() - diff --git a/src/proofing/migrations/0001_initial.py b/src/proofing/migrations/0001_initial.py index feae6649a6..6e99928f54 100755 --- a/src/proofing/migrations/0001_initial.py +++ b/src/proofing/migrations/0001_initial.py @@ -9,94 +9,207 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('core', '0001_initial'), - ('submission', '0001_initial'), + ("core", "0001_initial"), + ("submission", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Note', + name="Note", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField()), - ('date_time', models.DateTimeField(auto_now_add=True)), - ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='proofing_note_creator', to=settings.AUTH_USER_MODEL)), - ('galley', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Galley')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.TextField()), + ("date_time", models.DateTimeField(auto_now_add=True)), + ( + "creator", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="proofing_note_creator", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "galley", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.Galley" + ), + ), ], options={ - 'ordering': ('-date_time',), + "ordering": ("-date_time",), }, ), migrations.CreateModel( - name='ProofingAssignment', + name="ProofingAssignment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('completed', models.DateTimeField(blank=True, null=True)), - ('article', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('editor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='proofing_editor', to=settings.AUTH_USER_MODEL)), - ('proofing_manager', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("completed", models.DateTimeField(blank=True, null=True)), + ( + "article", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "editor", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="proofing_editor", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "proofing_manager", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='ProofingRound', + name="ProofingRound", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('number', models.PositiveIntegerField(default=1)), - ('date_started', models.DateTimeField(default=django.utils.timezone.now)), - ('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proofing.ProofingAssignment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("number", models.PositiveIntegerField(default=1)), + ( + "date_started", + models.DateTimeField(default=django.utils.timezone.now), + ), + ( + "assignment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="proofing.ProofingAssignment", + ), + ), ], options={ - 'ordering': ('-number',), + "ordering": ("-number",), }, ), migrations.CreateModel( - name='ProofingTask', + name="ProofingTask", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('due', models.DateTimeField(default=None, verbose_name='Date Due')), - ('accepted', models.DateTimeField(blank=True, null=True)), - ('completed', models.DateTimeField(blank=True, null=True)), - ('cancelled', models.BooleanField(default=False)), - ('acknowledged', models.DateTimeField(blank=True, null=True)), - ('task', models.TextField(verbose_name='Proofing Task')), - ('galleys_for_proofing', models.ManyToManyField(to='core.Galley')), - ('notes', models.ManyToManyField(to='proofing.Note')), - ('proofreader', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ('round', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proofing.ProofingRound')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("due", models.DateTimeField(default=None, verbose_name="Date Due")), + ("accepted", models.DateTimeField(blank=True, null=True)), + ("completed", models.DateTimeField(blank=True, null=True)), + ("cancelled", models.BooleanField(default=False)), + ("acknowledged", models.DateTimeField(blank=True, null=True)), + ("task", models.TextField(verbose_name="Proofing Task")), + ("galleys_for_proofing", models.ManyToManyField(to="core.Galley")), + ("notes", models.ManyToManyField(to="proofing.Note")), + ( + "proofreader", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "round", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="proofing.ProofingRound", + ), + ), ], ), migrations.CreateModel( - name='TypesetterProofingTask', + name="TypesetterProofingTask", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('due', models.DateTimeField(blank=True, null=True)), - ('accepted', models.DateTimeField(blank=True, null=True)), - ('completed', models.DateTimeField(blank=True, null=True)), - ('cancelled', models.BooleanField(default=False)), - ('acknowledged', models.DateTimeField(blank=True, null=True)), - ('task', models.TextField(verbose_name='Typesetter Task')), - ('notes', models.TextField(blank=True, null=True, verbose_name='Correction Note')), - ('galleys', models.ManyToManyField(to='core.Galley')), - ('proofing_task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proofing.ProofingTask')), - ('typesetter', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ("due", models.DateTimeField(blank=True, null=True)), + ("accepted", models.DateTimeField(blank=True, null=True)), + ("completed", models.DateTimeField(blank=True, null=True)), + ("cancelled", models.BooleanField(default=False)), + ("acknowledged", models.DateTimeField(blank=True, null=True)), + ("task", models.TextField(verbose_name="Typesetter Task")), + ( + "notes", + models.TextField( + blank=True, null=True, verbose_name="Correction Note" + ), + ), + ("galleys", models.ManyToManyField(to="core.Galley")), + ( + "proofing_task", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="proofing.ProofingTask", + ), + ), + ( + "typesetter", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Correction Task', + "verbose_name": "Correction Task", }, ), migrations.AlterUniqueTogether( - name='proofingassignment', - unique_together=set([('article', 'proofing_manager')]), + name="proofingassignment", + unique_together=set([("article", "proofing_manager")]), ), ] diff --git a/src/proofing/migrations/0002_proofingtask_proofed_files.py b/src/proofing/migrations/0002_proofingtask_proofed_files.py index a9c454a8ee..6c19a8477f 100644 --- a/src/proofing/migrations/0002_proofingtask_proofed_files.py +++ b/src/proofing/migrations/0002_proofingtask_proofed_files.py @@ -6,16 +6,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0013_auto_20180207_1525'), - ('proofing', '0001_initial'), + ("core", "0013_auto_20180207_1525"), + ("proofing", "0001_initial"), ] operations = [ migrations.AddField( - model_name='proofingtask', - name='proofed_files', - field=models.ManyToManyField(to='core.File'), + model_name="proofingtask", + name="proofed_files", + field=models.ManyToManyField(to="core.File"), ), ] diff --git a/src/proofing/migrations/0003_typesetterproofingtask_files.py b/src/proofing/migrations/0003_typesetterproofingtask_files.py index f859951806..2bfe5b43b7 100644 --- a/src/proofing/migrations/0003_typesetterproofingtask_files.py +++ b/src/proofing/migrations/0003_typesetterproofingtask_files.py @@ -6,16 +6,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0013_auto_20180207_1525'), - ('proofing', '0002_proofingtask_proofed_files'), + ("core", "0013_auto_20180207_1525"), + ("proofing", "0002_proofingtask_proofed_files"), ] operations = [ migrations.AddField( - model_name='typesetterproofingtask', - name='files', - field=models.ManyToManyField(to='core.File'), + model_name="typesetterproofingtask", + name="files", + field=models.ManyToManyField(to="core.File"), ), ] diff --git a/src/proofing/migrations/0004_alter_proofingassignment_editor.py b/src/proofing/migrations/0004_alter_proofingassignment_editor.py index 8466f6db02..a8f8961e10 100644 --- a/src/proofing/migrations/0004_alter_proofingassignment_editor.py +++ b/src/proofing/migrations/0004_alter_proofingassignment_editor.py @@ -6,16 +6,20 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('proofing', '0003_typesetterproofingtask_files'), + ("proofing", "0003_typesetterproofingtask_files"), ] operations = [ migrations.AlterField( - model_name='proofingassignment', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='proofing_editor', to=settings.AUTH_USER_MODEL), + model_name="proofingassignment", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="proofing_editor", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/proofing/models.py b/src/proofing/models.py index 74952dc9ad..2f87314336 100755 --- a/src/proofing/models.py +++ b/src/proofing/models.py @@ -13,14 +13,16 @@ class ProofingAssignment(models.Model): article = models.OneToOneField( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) - proofing_manager = models.ForeignKey('core.Account', null=True, on_delete=models.SET_NULL) + proofing_manager = models.ForeignKey( + "core.Account", null=True, on_delete=models.SET_NULL + ) editor = models.ForeignKey( - 'core.Account', + "core.Account", null=True, - related_name='proofing_editor', + related_name="proofing_editor", on_delete=models.SET_NULL, ) assigned = models.DateTimeField(default=timezone.now) @@ -28,25 +30,24 @@ class ProofingAssignment(models.Model): completed = models.DateTimeField(blank=True, null=True) class Meta: - unique_together = ('article', 'proofing_manager') + unique_together = ("article", "proofing_manager") @property def current_proofing_round_number(self): try: - return self.proofinground_set.all().order_by('-number')[0].number + return self.proofinground_set.all().order_by("-number")[0].number except IndexError: return 0 def current_proofing_round(self): try: - return self.proofinground_set.all().order_by('-number')[0] + return self.proofinground_set.all().order_by("-number")[0] except IndexError: return None def add_new_proofing_round(self): new_round_number = self.current_proofing_round_number + 1 - return ProofingRound.objects.create(assignment=self, - number=new_round_number) + return ProofingRound.objects.create(assignment=self, number=new_round_number) def user_is_manager(self, user): if user == self.proofing_manager: @@ -54,7 +55,7 @@ def user_is_manager(self, user): return False def __str__(self): - return 'Proofing Assignment {pk}'.format(pk=self.pk) + return "Proofing Assignment {pk}".format(pk=self.pk) class ProofingRound(models.Model): @@ -66,10 +67,12 @@ class ProofingRound(models.Model): date_started = models.DateTimeField(default=timezone.now) class Meta: - ordering = ('-number',) + ordering = ("-number",) def __str__(self): - return "Round #{0} for Article {1}".format(self.number, self.assignment.article.title) + return "Round #{0} for Article {1}".format( + self.number, self.assignment.article.title + ) @property def has_active_tasks(self): @@ -92,13 +95,12 @@ def typeset_tasks(self): return typeset_tasks def delete_round_relations(self, request, article, tasks, corrections): - for task in tasks: if not task.completed: kwargs = { - 'article': article, - 'proofing_task': task, - 'request': request, + "article": article, + "proofing_task": task, + "request": request, } event_logic.Events.raise_event( event_logic.Events.ON_CANCEL_PROOFING_TASK, @@ -110,9 +112,9 @@ def delete_round_relations(self, request, article, tasks, corrections): for correction in corrections: if not correction.completed and not correction.cancelled: kwargs = { - 'article': article, - 'correction': correction, - 'request': request, + "article": article, + "correction": correction, + "request": request, } event_logic.Events.raise_event( event_logic.Events.ON_CORRECTIONS_CANCELLED, @@ -129,13 +131,12 @@ def can_add_another_proofreader(self, journal): """ limit = setting_handler.get_setting( - 'general', - 'max_proofreaders', + "general", + "max_proofreaders", journal, ).processed_value if not limit == 0: - current_num_proofers = ProofingTask.objects.filter( round=self, ).count() @@ -148,14 +149,23 @@ def can_add_another_proofreader(self, journal): class ActiveProofingTaskManager(models.Manager): def get_queryset(self): - return super(ActiveProofingTaskManager, self).get_queryset().exclude( - round__assignment__article__stage=submission_models.STAGE_ARCHIVED, + return ( + super(ActiveProofingTaskManager, self) + .get_queryset() + .exclude( + round__assignment__article__stage=submission_models.STAGE_ARCHIVED, + ) ) class ProofingTask(models.Model): - round = models.ForeignKey(ProofingRound, on_delete=models.CASCADE,) - proofreader = models.ForeignKey('core.Account', null=True, on_delete=models.SET_NULL) + round = models.ForeignKey( + ProofingRound, + on_delete=models.CASCADE, + ) + proofreader = models.ForeignKey( + "core.Account", null=True, on_delete=models.SET_NULL + ) assigned = models.DateTimeField(default=timezone.now) notified = models.BooleanField(default=False) due = models.DateTimeField(default=None, verbose_name="Date Due") @@ -165,17 +175,19 @@ class ProofingTask(models.Model): acknowledged = models.DateTimeField(blank=True, null=True) task = models.TextField(verbose_name="Proofing Task") - galleys_for_proofing = models.ManyToManyField('core.Galley') - proofed_files = models.ManyToManyField('core.File') - notes = models.ManyToManyField('proofing.Note') + galleys_for_proofing = models.ManyToManyField("core.Galley") + proofed_files = models.ManyToManyField("core.File") + notes = models.ManyToManyField("proofing.Note") objects = models.Manager() active_objects = ActiveProofingTaskManager() def __str__(self): - return "{0} proofing {1} in round {2}".format(self.proofreader.full_name(), - self.round.assignment.article.title, - self.round.number) + return "{0} proofing {1} in round {2}".format( + self.proofreader.full_name(), + self.round.assignment.article.title, + self.round.number, + ) @property def assignment(self): @@ -186,15 +198,15 @@ def typesetter_tasks(self): def status(self): if self.cancelled: - return {'slug': 'cancelled', 'friendly': 'Task cancelled'} + return {"slug": "cancelled", "friendly": "Task cancelled"} elif self.assigned and not self.accepted and not self.completed: - return {'slug': 'assigned', 'friendly': 'Awaiting response'} + return {"slug": "assigned", "friendly": "Awaiting response"} elif self.assigned and self.accepted and not self.completed: - return {'slug': 'accepted', 'friendly': 'Task accepted, underway'} + return {"slug": "accepted", "friendly": "Task accepted, underway"} elif self.assigned and not self.accepted and self.completed: - return {'slug': 'declined', 'friendly': 'Task declined'} + return {"slug": "declined", "friendly": "Task declined"} elif self.completed: - return {'slug': 'completed', 'friendly': 'Task completed'} + return {"slug": "completed", "friendly": "Task completed"} def galley_files(self): return [galley.file for galley in self.galleys_for_proofing.all()] @@ -203,11 +215,14 @@ def actor(self): return self.proofreader def review_comments(self): - comment_text = '' - for note in self.notes.all().order_by('galley'): - comment_text = comment_text + "Comment by: {0} for Galley {1}
{2}
".format(note.creator.full_name(), - note.galley, - note.text) + comment_text = "" + for note in self.notes.all().order_by("galley"): + comment_text = ( + comment_text + + "Comment by: {0} for Galley {1}
{2}
".format( + note.creator.full_name(), note.galley, note.text + ) + ) return comment_text @@ -220,14 +235,18 @@ def reset(self): class ActiveTypesetterProofingTaskManager(models.Manager): def get_queryset(self): - return super(ActiveTypesetterProofingTaskManager, self).get_queryset().exclude( - proofing_task__round__assignment__article__stage=submission_models.STAGE_ARCHIVED, + return ( + super(ActiveTypesetterProofingTaskManager, self) + .get_queryset() + .exclude( + proofing_task__round__assignment__article__stage=submission_models.STAGE_ARCHIVED, + ) ) class TypesetterProofingTask(models.Model): proofing_task = models.ForeignKey(ProofingTask, on_delete=models.CASCADE) - typesetter = models.ForeignKey('core.Account', null=True, on_delete=models.SET_NULL) + typesetter = models.ForeignKey("core.Account", null=True, on_delete=models.SET_NULL) assigned = models.DateTimeField(default=timezone.now) notified = models.BooleanField(default=False) due = models.DateTimeField(blank=True, null=True) @@ -237,46 +256,50 @@ class TypesetterProofingTask(models.Model): acknowledged = models.DateTimeField(blank=True, null=True) task = models.TextField(verbose_name="Typesetter Task") - galleys = models.ManyToManyField('core.Galley') - files = models.ManyToManyField('core.File') + galleys = models.ManyToManyField("core.Galley") + files = models.ManyToManyField("core.File") notes = models.TextField(verbose_name="Correction Note", blank=True, null=True) objects = models.Manager() active_objects = ActiveTypesetterProofingTaskManager() class Meta: - verbose_name = 'Correction Task' + verbose_name = "Correction Task" def __str__(self): - return "Correction Task Proof ID: {0}, Proofreader {1}, Due: {2}".format(self.proofing_task.pk, - self.typesetter.full_name(), - self.due) + return "Correction Task Proof ID: {0}, Proofreader {1}, Due: {2}".format( + self.proofing_task.pk, self.typesetter.full_name(), self.due + ) def status(self): if self.cancelled: - return {'slug': 'cancelled', 'friendly': 'Cancelled'} + return {"slug": "cancelled", "friendly": "Cancelled"} elif self.assigned and not self.accepted and not self.completed: - return {'slug': 'assigned', 'friendly': 'Awaiting response'} + return {"slug": "assigned", "friendly": "Awaiting response"} elif self.assigned and self.accepted and not self.completed: - return {'slug': 'accepted', 'friendly': 'Underway'} + return {"slug": "accepted", "friendly": "Underway"} elif self.assigned and not self.accepted and self.completed: - return {'slug': 'declined', 'friendly': 'Declined'} + return {"slug": "declined", "friendly": "Declined"} elif self.completed: - return {'slug': 'completed', 'friendly': 'Completed'} + return {"slug": "completed", "friendly": "Completed"} def actor(self): return self.typesetter class Note(models.Model): - galley = models.ForeignKey('core.Galley', on_delete=models.CASCADE) - creator = models.ForeignKey('core.Account', related_name='proofing_note_creator', - null=True, on_delete=models.SET_NULL) + galley = models.ForeignKey("core.Galley", on_delete=models.CASCADE) + creator = models.ForeignKey( + "core.Account", + related_name="proofing_note_creator", + null=True, + on_delete=models.SET_NULL, + ) text = models.TextField() date_time = models.DateTimeField(auto_now_add=True) class Meta: - ordering = ('-date_time',) + ordering = ("-date_time",) def __str__(self): return "{0} - {1} {2}".format(self.pk, self.creator.full_name(), self.galley) diff --git a/src/proofing/tests.py b/src/proofing/tests.py index 1d452f9751..a108d7a7d2 100644 --- a/src/proofing/tests.py +++ b/src/proofing/tests.py @@ -8,27 +8,26 @@ class TestLogic(TestCase): - @classmethod def setUpTestData(cls): cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() cls.editor = helpers.create_user( - 'typesetter@janeway.systems', - roles=['editor'], + "typesetter@janeway.systems", + roles=["editor"], journal=cls.journal_one, - atrrs={'is_active': True} + atrrs={"is_active": True}, ) cls.proofreader = helpers.create_user( - 'proofreader@janeway.systems', - roles=['proofreader'], + "proofreader@janeway.systems", + roles=["proofreader"], journal=cls.journal_one, ) cls.proofreader.is_active = True cls.proofreader.save() cls.typesetter = helpers.create_user( - 'typesetter@janeway.systems', - roles=['typesetter'], + "typesetter@janeway.systems", + roles=["typesetter"], journal=cls.journal_one, ) cls.typesetter.is_active = True @@ -36,13 +35,13 @@ def setUpTestData(cls): cls.active_article = helpers.create_article( journal=cls.journal_one, ) - cls.active_article.title = 'Active Article' + cls.active_article.title = "Active Article" cls.active_article.save() cls.archived_article = helpers.create_article( journal=cls.journal_one, ) cls.archived_article.stage = submission_models.STAGE_ARCHIVED - cls.archived_article.title = 'Archived Article' + cls.archived_article.title = "Archived Article" cls.archived_article.save() cls.active_proofing_assignment = models.ProofingAssignment.objects.create( @@ -57,7 +56,7 @@ def setUpTestData(cls): cls.active_proofing_task = models.ProofingTask.objects.create( round=cls.active_round, proofreader=cls.proofreader, - task='Active Proofing Task', + task="Active Proofing Task", due=timezone.now(), ) @@ -73,43 +72,35 @@ def setUpTestData(cls): cls.archived_proofing_task = models.ProofingTask.objects.create( round=cls.archived_round, proofreader=cls.proofreader, - task='Archived Proofing Task', + task="Archived Proofing Task", due=timezone.now(), ) cls.active_correction = models.TypesetterProofingTask.objects.create( proofing_task=cls.active_proofing_task, typesetter=cls.typesetter, - task='Active Correction', + task="Active Correction", ) cls.archived_correction = models.TypesetterProofingTask.objects.create( proofing_task=cls.archived_proofing_task, typesetter=cls.typesetter, - task='Active Correction', + task="Active Correction", ) def test_archive_stage_hides_task(self): self.client.force_login(self.proofreader) - response = self.client.get( - reverse('proofing_requests') - ) + response = self.client.get(reverse("proofing_requests")) self.assertContains( response, - 'Active Article', - ) - self.assertNotContains( - response, - 'Archived Article' + "Active Article", ) + self.assertNotContains(response, "Archived Article") def test_archived_article_task_404s(self): self.client.force_login(self.proofreader) response = self.client.get( reverse( - 'do_proofing', - kwargs={ - 'proofing_task_id': self.active_proofing_task.pk - } + "do_proofing", kwargs={"proofing_task_id": self.active_proofing_task.pk} ) ) self.assertTrue( @@ -121,10 +112,8 @@ def test_active_article_task_200s(self): self.client.force_login(self.proofreader) response = self.client.get( reverse( - 'do_proofing', - kwargs={ - 'proofing_task_id': self.archived_proofing_task.pk - } + "do_proofing", + kwargs={"proofing_task_id": self.archived_proofing_task.pk}, ) ) self.assertTrue( @@ -134,26 +123,22 @@ def test_active_article_task_200s(self): def test_archive_stage_hides_correction_task(self): self.client.force_login(self.typesetter) - response = self.client.get( - reverse('proofing_correction_requests') - ) + response = self.client.get(reverse("proofing_correction_requests")) self.assertContains( response, - f'Article ID: {self.active_article.id}', + f"Article ID: {self.active_article.id}", ) self.assertNotContains( response, - f'Article ID: {self.archived_article.id}', + f"Article ID: {self.archived_article.id}", ) def test_archived_article_correction_404s(self): self.client.force_login(self.typesetter) response = self.client.get( reverse( - 'typesetting_corrections', - kwargs={ - 'typeset_task_id': self.archived_correction.pk - } + "typesetting_corrections", + kwargs={"typeset_task_id": self.archived_correction.pk}, ) ) self.assertTrue( @@ -165,10 +150,8 @@ def test_active_article_correction_200s(self): self.client.force_login(self.typesetter) response = self.client.get( reverse( - 'typesetting_corrections', - kwargs={ - 'typeset_task_id': self.active_correction.pk - } + "typesetting_corrections", + kwargs={"typeset_task_id": self.active_correction.pk}, ) ) self.assertTrue( diff --git a/src/proofing/urls.py b/src/proofing/urls.py index 830e528f47..5a309d3b9b 100755 --- a/src/proofing/urls.py +++ b/src/proofing/urls.py @@ -8,60 +8,107 @@ urlpatterns = [ # PM - re_path(r'^$', views.proofing_list, name='proofing_list'), - re_path(r'^(?P\d+)/assign_manager/(?P\d+)/$', views.proofing_assign_article, - name='proofing_assign_article_with_user'), - re_path(r'^(?P\d+)/$', views.proofing_article, name='proofing_article'), - re_path(r'^unassign/(?P\d+)/$', views.proofing_unassign_article, name='proofing_unassign_article'), - re_path(r'^(?P\d+)/proofing_task/(?P\d+)/notify/$', views.notify_proofreader, - name='notify_proofreader'), - re_path(r'^(?P\d+)/proofing_task/(?P\d+)/edit/$', views.edit_proofing_assignment, - name='edit_proofing_assignment'), - - re_path(r'^(?P\d+)/round/(?P\d+)/edit/$', + re_path(r"^$", views.proofing_list, name="proofing_list"), + re_path( + r"^(?P\d+)/assign_manager/(?P\d+)/$", + views.proofing_assign_article, + name="proofing_assign_article_with_user", + ), + re_path(r"^(?P\d+)/$", views.proofing_article, name="proofing_article"), + re_path( + r"^unassign/(?P\d+)/$", + views.proofing_unassign_article, + name="proofing_unassign_article", + ), + re_path( + r"^(?P\d+)/proofing_task/(?P\d+)/notify/$", + views.notify_proofreader, + name="notify_proofreader", + ), + re_path( + r"^(?P\d+)/proofing_task/(?P\d+)/edit/$", + views.edit_proofing_assignment, + name="edit_proofing_assignment", + ), + re_path( + r"^(?P\d+)/round/(?P\d+)/edit/$", views.delete_proofing_round, - name='delete_proofing_round'), - - re_path(r'^(?P\d+)/proofing_task/(?P\d+)/review/$', views.do_proofing, - name='review_proofing_task'), - re_path(r'^(?P\d+)/proofing_task/(?P\d+)/corrections/$', + name="delete_proofing_round", + ), + re_path( + r"^(?P\d+)/proofing_task/(?P\d+)/review/$", + views.do_proofing, + name="review_proofing_task", + ), + re_path( + r"^(?P\d+)/proofing_task/(?P\d+)/corrections/$", views.request_typesetting_changes, - name='request_typesetting_changes'), - re_path(r'^(?P\d+)/proofing_task/(?P\d+)/corrections/(?P\d+)/notify/$', + name="request_typesetting_changes", + ), + re_path( + r"^(?P\d+)/proofing_task/(?P\d+)/corrections/(?P\d+)/notify/$", views.notify_typesetter_changes, - name='notify_typesetter_changes'), - re_path(r'^(?P\d+)/ack/(?Pproofing|correction)/id/(?P\d+)/$', + name="notify_typesetter_changes", + ), + re_path( + r"^(?P\d+)/ack/(?Pproofing|correction)/id/(?P\d+)/$", views.acknowledge, - name='acknowledge_proofing'), - re_path(r'^(?P\d+)/complete/$', views.complete_proofing, name='complete_proofing'), - + name="acknowledge_proofing", + ), + re_path( + r"^(?P\d+)/complete/$", + views.complete_proofing, + name="complete_proofing", + ), # Proofreader - re_path(r'^requests/$', views.proofing_requests, name='proofing_requests'), - re_path(r'^requests/(?P\d+)/$', views.do_proofing, name='do_proofing'), - re_path(r'^requests/(?P\d+)/decision/(?Paccept|decline)/$', + re_path(r"^requests/$", views.proofing_requests, name="proofing_requests"), + re_path( + r"^requests/(?P\d+)/$", views.do_proofing, name="do_proofing" + ), + re_path( + r"^requests/(?P\d+)/decision/(?Paccept|decline)/$", views.proofing_requests, - name='proofing_requests_decision'), - re_path(r'^requests/(?P\d+)/file/(?P\d+)/download/$', views.proofing_download, - name='proofing_download'), - re_path(r'^requests/(?P\d+)/file/(?P\d+)/download/galley.epub$', views.proofing_download, - name='proofing_epub_download'), - re_path(r'^requests/(?P\d+)/preview/(?P\d+)/$', views.preview_galley, - name='preview_galley'), - re_path(r'^requests/(?P\d+)/galley/(?P\d+)/new_note/$', + name="proofing_requests_decision", + ), + re_path( + r"^requests/(?P\d+)/file/(?P\d+)/download/$", + views.proofing_download, + name="proofing_download", + ), + re_path( + r"^requests/(?P\d+)/file/(?P\d+)/download/galley.epub$", + views.proofing_download, + name="proofing_epub_download", + ), + re_path( + r"^requests/(?P\d+)/preview/(?P\d+)/$", + views.preview_galley, + name="preview_galley", + ), + re_path( + r"^requests/(?P\d+)/galley/(?P\d+)/new_note/$", views.new_note, - name='proofing_new_note'), - re_path(r'^requests/(?P\d+)/galley/(?P\d+)/delete/$', + name="proofing_new_note", + ), + re_path( + r"^requests/(?P\d+)/galley/(?P\d+)/delete/$", views.delete_note, - name='proofing_delete_note'), - - re_path(r'^requests/(?P\d+)/preview/(?P\d+)/(?P.*)$', views.preview_figure, - name='preview_figure'), - + name="proofing_delete_note", + ), + re_path( + r"^requests/(?P\d+)/preview/(?P\d+)/(?P.*)$", + views.preview_figure, + name="preview_figure", + ), # Corrections - re_path(r'^requests/corrections/$', + re_path( + r"^requests/corrections/$", views.correction_requests, - name='proofing_correction_requests'), - re_path(r'^requests/corrections/(?P\d+)/$', + name="proofing_correction_requests", + ), + re_path( + r"^requests/corrections/(?P\d+)/$", views.typesetting_corrections, - name='typesetting_corrections'), + name="typesetting_corrections", + ), ] diff --git a/src/proofing/views.py b/src/proofing/views.py index a222022c7c..6439b4e0b8 100755 --- a/src/proofing/views.py +++ b/src/proofing/views.py @@ -15,9 +15,12 @@ from proofing import models as proofing_models from security.decorators import ( - proofing_manager_or_editor_required, proofing_manager_for_article_required, - typesetter_user_required, proofreader_for_article_required, - typesetter_for_corrections_required, proofing_manager_roles, + proofing_manager_or_editor_required, + proofing_manager_for_article_required, + typesetter_user_required, + proofreader_for_article_required, + typesetter_for_corrections_required, + proofing_manager_roles, ) from submission import models as submission_models from core import models as core_models, files @@ -35,27 +38,30 @@ def proofing_list(request): :return: HttpResponse object """ assigned_table = proofing_models.ProofingAssignment.objects.all() - my_table = proofing_models.ProofingAssignment.objects.values_list('article_id', flat=True).filter( - proofing_manager=request.user) + my_table = proofing_models.ProofingAssignment.objects.values_list( + "article_id", flat=True + ).filter(proofing_manager=request.user) assigned = [assignment.article.pk for assignment in assigned_table] - unassigned_articles = submission_models.Article.objects.filter(stage=submission_models.STAGE_PROOFING, - journal=request.journal).exclude( - id__in=assigned) - assigned_articles = submission_models.Article.objects.filter(stage=submission_models.STAGE_PROOFING, - journal=request.journal).exclude( - id__in=unassigned_articles) - - my_articles = submission_models.Article.objects.filter(stage=submission_models.STAGE_PROOFING, - journal=request.journal, - id__in=my_table) - - template = 'proofing/index.html' + unassigned_articles = submission_models.Article.objects.filter( + stage=submission_models.STAGE_PROOFING, journal=request.journal + ).exclude(id__in=assigned) + assigned_articles = submission_models.Article.objects.filter( + stage=submission_models.STAGE_PROOFING, journal=request.journal + ).exclude(id__in=unassigned_articles) + + my_articles = submission_models.Article.objects.filter( + stage=submission_models.STAGE_PROOFING, journal=request.journal, id__in=my_table + ) + + template = "proofing/index.html" context = { - 'proofing_articles': unassigned_articles, - 'assigned_articles': assigned_articles, - 'my_articles': my_articles, - 'production_managers': core_models.AccountRole.objects.filter(role__slug='proofing-manager') + "proofing_articles": unassigned_articles, + "assigned_articles": assigned_articles, + "my_articles": my_articles, + "production_managers": core_models.AccountRole.objects.filter( + role__slug="proofing-manager" + ), } return render(request, template, context) @@ -70,30 +76,39 @@ def proofing_assign_article(request, article_id, user_id=None): :param user_id: Account object PK :return: HttpRedirect """ - article = get_object_or_404(submission_models.Article, pk=article_id, journal=request.journal) + article = get_object_or_404( + submission_models.Article, pk=article_id, journal=request.journal + ) user = get_object_or_404(core_models.Account, pk=user_id) if user.is_proofing_manager: - proofing_assignment = models.ProofingAssignment.objects.create(article=article, - proofing_manager=user, - notified=True, - editor=request.user) + proofing_assignment = models.ProofingAssignment.objects.create( + article=article, proofing_manager=user, notified=True, editor=request.user + ) proofing_assignment.add_new_proofing_round() message = "{0} has been assigned as proofing manager to {1}".format( proofing_assignment.proofing_manager.full_name(), - proofing_assignment.article.title) + proofing_assignment.article.title, + ) messages.add_message(request, messages.INFO, message) kwargs = { - 'request': request, 'proofing_assignment': proofing_assignment, + "request": request, + "proofing_assignment": proofing_assignment, } - event_logic.Events.raise_event(event_logic.Events.ON_PROOFING_MANAGER_ASSIGNMENT, task_object=article, **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFING_MANAGER_ASSIGNMENT, + task_object=article, + **kwargs, + ) else: - messages.add_message(request, messages.WARNING, 'User is not a proofing manager.') + messages.add_message( + request, messages.WARNING, "User is not a proofing manager." + ) - return redirect(reverse('proofing_list')) + return redirect(reverse("proofing_list")) @proofing_manager_or_editor_required @@ -104,14 +119,20 @@ def proofing_unassign_article(request, article_id): :param article_id: Article object PK :return: HttpRedirect """ - article = submission_models.Article.objects.get(id=article_id, journal=request.journal) + article = submission_models.Article.objects.get( + id=article_id, journal=request.journal + ) if not article.proofingassignment.current_proofing_round().proofingtask_set.all(): article.proofingassignment.delete() else: - messages.add_message(request, messages.WARNING, 'This assignment has active tasks, cannot be deleted.') + messages.add_message( + request, + messages.WARNING, + "This assignment has active tasks, cannot be deleted.", + ) - return redirect('proofing_list') + return redirect("proofing_list") @proofing_manager_for_article_required @@ -123,9 +144,7 @@ def proofing_article(request, article_id): :return: HttpRedirect if POST or HttpResponse """ article = get_object_or_404( - submission_models.Article.objects.select_related( - 'productionassignment' - ), + submission_models.Article.objects.select_related("productionassignment"), pk=article_id, journal=request.journal, ) @@ -138,35 +157,33 @@ def proofing_article(request, article_id): modal = None if request.POST: - - if 'new-round' in request.POST: + if "new-round" in request.POST: logic.handle_closing_active_task(request, article) new_round = article.proofingassignment.add_new_proofing_round() messages.add_message( request, messages.SUCCESS, - 'New round {0} added.'.format(new_round.number), + "New round {0} added.".format(new_round.number), ) return redirect( reverse( - 'proofing_article', - kwargs={'article_id': article.pk}, + "proofing_article", + kwargs={"article_id": article.pk}, ) ) - if 'new-proofreader' in request.POST: - + if "new-proofreader" in request.POST: if not current_round.can_add_another_proofreader(request.journal): messages.add_message( request, messages.WARNING, - 'The number of proofreaders per round has been limited.' - ' You cannot add another proofreader in this round.', + "The number of proofreaders per round has been limited." + " You cannot add another proofreader in this round.", ) return redirect( reverse( - 'proofing_article', - kwargs={'article_id': article.pk}, + "proofing_article", + kwargs={"article_id": article.pk}, ) ) @@ -175,10 +192,10 @@ def proofing_article(request, article_id): galleys = logic.get_galleys_from_post(request) if not user: - form.add_error(None, 'You must select a user.') + form.add_error(None, "You must select a user.") if not galleys: - form.add_error(None, 'You must select at least one Galley.') + form.add_error(None, "You must select at least one Galley.") if form.is_valid(): proofing_task = form.save(commit=False) @@ -188,25 +205,25 @@ def proofing_article(request, article_id): proofing_task.galleys_for_proofing.add(*galleys) return redirect( reverse( - 'notify_proofreader', + "notify_proofreader", kwargs={ - 'article_id': article.pk, - 'proofing_task_id': proofing_task.pk, - } + "article_id": article.pk, + "proofing_task_id": proofing_task.pk, + }, ) ) # Set the modal to open if this page is not redirected. - modal = 'add_proofer' + modal = "add_proofer" - template = 'proofing/proofing_article.html' + template = "proofing/proofing_article.html" context = { - 'article': article, - 'proofreaders': proofreaders, - 'form': form, - 'modal': modal, - 'user': user if request.POST else None, - 'galleys': galleys if request.POST else None + "article": article, + "proofreaders": proofreaders, + "form": form, + "modal": modal, + "user": user if request.POST else None, + "galleys": galleys if request.POST else None, } return render(request, template, context) @@ -252,21 +269,16 @@ def delete_proofing_round(request, article_id, round_id): messages.add_message( request, messages.INFO, - 'Proofing Round Deleted', - ) - return redirect( - reverse( - 'proofing_article', - kwargs={'article_id': article.pk} - ) + "Proofing Round Deleted", ) + return redirect(reverse("proofing_article", kwargs={"article_id": article.pk})) - template = 'proofing/delete_proofing_round.html' + template = "proofing/delete_proofing_round.html" context = { - 'article': article, - 'round': round, - 'proofing_tasks': proofing_tasks, - 'correction_tasks': correction_tasks, + "article": article, + "round": round, + "proofing_tasks": proofing_tasks, + "correction_tasks": correction_tasks, } return render(request, template, context) @@ -286,17 +298,17 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): pk=article_id, journal=request.journal, ) - proofing_task = get_object_or_404(models.ProofingTask, - pk=proofing_task_id) + proofing_task = get_object_or_404(models.ProofingTask, pk=proofing_task_id) form = forms.AssignProofreader(instance=proofing_task) if request.POST: - - if 'delete' in request.POST: - kwargs = {'article': article, - 'proofing_task': proofing_task, - 'request': request} + if "delete" in request.POST: + kwargs = { + "article": article, + "proofing_task": proofing_task, + "request": request, + } event_logic.Events.raise_event( event_logic.Events.ON_CANCEL_PROOFING_TASK, task_object=article, @@ -306,17 +318,17 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): messages.add_message( request, messages.SUCCESS, - 'Proofing task deleted.', + "Proofing task deleted.", ) return redirect( reverse( - 'proofing_article', - kwargs={'article_id': article.id}, + "proofing_article", + kwargs={"article_id": article.id}, ) ) - if 'reset' in request.POST: + if "reset" in request.POST: proofing_task.reset() logic.add_reset_log_entry( request, @@ -326,12 +338,12 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): messages.add_message( request, messages.INFO, - 'Proofing task reset.', + "Proofing task reset.", ) return redirect( reverse( - 'proofing_article', - kwargs={'article_id': article.id}, + "proofing_article", + kwargs={"article_id": article.id}, ) ) @@ -342,7 +354,7 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): galleys = logic.get_galleys_from_post(request) if not galleys: - form.add_error(None, 'You must select at least one Galley.') + form.add_error(None, "You must select at least one Galley.") if form.is_valid(): proofing_task = form.save() @@ -354,9 +366,9 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): proofing_task.galleys_for_proofing.add(*galleys) kwargs = { - 'article': article, - 'proofing_task': proofing_task, - 'request': request + "article": article, + "proofing_task": proofing_task, + "request": request, } event_logic.Events.raise_event( event_logic.Events.ON_EDIT_PROOFING_TASK, @@ -364,14 +376,16 @@ def edit_proofing_assignment(request, article_id, proofing_task_id): **kwargs, ) - return redirect(reverse('proofing_article', kwargs={'article_id': article.id})) + return redirect( + reverse("proofing_article", kwargs={"article_id": article.id}) + ) - template = 'proofing/edit_proofing_assignment.html' + template = "proofing/edit_proofing_assignment.html" context = { - 'article': article, - 'proofing_task': proofing_task, - 'form': form, - 'galleys': proofing_task.galleys_for_proofing.all(), + "article": article, + "proofing_task": proofing_task, + "form": form, + "galleys": proofing_task.galleys_for_proofing.all(), } return render(request, template, context) @@ -387,7 +401,7 @@ def notify_proofreader(request, article_id, proofing_task_id): :return: HttpRedirect or HttpResponse """ article = get_object_or_404( - submission_models.Article.objects.select_related('productionassignment'), + submission_models.Article.objects.select_related("productionassignment"), pk=article_id, journal=request.journal, ) @@ -396,22 +410,22 @@ def notify_proofreader(request, article_id, proofing_task_id): if request.POST: kwargs = { - 'request': request, - 'user_content_message': request.POST.get('content_email'), - 'article': article, - 'proofing_task': proofing_task, - 'skip': True if 'skip' in request.POST else False + "request": request, + "user_content_message": request.POST.get("content_email"), + "article": article, + "proofing_task": proofing_task, + "skip": True if "skip" in request.POST else False, } - event_logic.Events.raise_event(event_logic.Events.ON_NOTIFY_PROOFREADER, - task_object=article, - **kwargs) - return redirect(reverse('proofing_article', kwargs={'article_id': article.id})) + event_logic.Events.raise_event( + event_logic.Events.ON_NOTIFY_PROOFREADER, task_object=article, **kwargs + ) + return redirect(reverse("proofing_article", kwargs={"article_id": article.id})) - template = 'proofing/notify_proofreader.html' + template = "proofing/notify_proofreader.html" context = { - 'article': article, - 'proofing_task': proofing_task, - 'user_message_content': user_message_content + "article": article, + "proofing_task": proofing_task, + "user_message_content": user_message_content, } return render(request, template, context) @@ -427,20 +441,18 @@ def proofing_requests(request, proofing_task_id=None, decision=None): :return: HttpResponse or HttpRedirect """ if proofing_task_id: - if proofing_task_id: logic.handle_proof_decision(request, proofing_task_id, decision) - return redirect(reverse('proofing_requests')) + return redirect(reverse("proofing_requests")) new, active, completed = logic.get_tasks(request) - template = 'proofing/proofing_requests.html' + template = "proofing/proofing_requests.html" context = { - 'new_requests': new, - 'active_requests': active, - 'completed_requests': completed, - + "new_requests": new, + "active_requests": active, + "completed_requests": completed, } return render(request, template, context) @@ -448,29 +460,26 @@ def proofing_requests(request, proofing_task_id=None, decision=None): @typesetter_user_required def correction_requests(request): - if request.POST: - typeset_task_id = request.POST.get('typeset_task_id', None) - decision = request.POST.get('decision', None) + typeset_task_id = request.POST.get("typeset_task_id", None) + decision = request.POST.get("decision", None) if typeset_task_id and decision: logic.handle_typeset_decision(request, typeset_task_id, decision) else: messages.add_message( - request, - messages.WARNING, - 'Task ID or Decision missing.' + request, messages.WARNING, "Task ID or Decision missing." ) - return redirect(reverse('proofing_correction_requests')) + return redirect(reverse("proofing_correction_requests")) new, active, completed = logic.get_typesetting_tasks(request) - template = 'proofing/correction_requests.html' + template = "proofing/correction_requests.html" context = { - 'new_typesetting_requests': new, - 'active_typesetting_requests': active, - 'completed_typesetting_requests': completed, + "new_typesetting_requests": new, + "active_typesetting_requests": active, + "completed_typesetting_requests": completed, } return render(request, template, context) @@ -504,24 +513,28 @@ def do_proofing(request, proofing_task_id, article_id=None): article = proofing_task.round.assignment.article - if request.POST and 'complete' in request.POST: + if request.POST and "complete" in request.POST: proofing_task.completed = timezone.now() proofing_task.save() - kwargs = {'proofing_task': proofing_task, 'article': article, 'request': request} - event_logic.Events.raise_event(event_logic.Events.ON_COMPLETE_PROOFING_TASK, - task_object=article, - **kwargs) - return redirect(reverse('proofing_requests')) + kwargs = { + "proofing_task": proofing_task, + "article": article, + "request": request, + } + event_logic.Events.raise_event( + event_logic.Events.ON_COMPLETE_PROOFING_TASK, task_object=article, **kwargs + ) + return redirect(reverse("proofing_requests")) - elif request.POST and 'upload' in request.POST: + elif request.POST and "upload" in request.POST: modal = logic.handle_annotated_galley_upload(request, proofing_task, article) - template = 'proofing/do_proofing.html' + template = "proofing/do_proofing.html" context = { - 'proofing_task': proofing_task, - 'article': article, - 'proofing_manager': proofing_manager, - 'modal': modal + "proofing_task": proofing_task, + "article": article, + "proofing_manager": proofing_manager, + "modal": modal, } return render(request, template, context) @@ -540,26 +553,26 @@ def preview_galley(request, proofing_task_id, galley_id): galley = get_object_or_404(proofing_task.galleys_for_proofing, pk=galley_id) article_content = "" - if galley.type == 'xml' or galley.type == 'html': - template = 'proofing/preview/rendered.html' + if galley.type == "xml" or galley.type == "html": + template = "proofing/preview/rendered.html" try: article_content = galley.file_content() except Exception as e: messages.add_message( request, messages.ERROR, - 'Errors found rendering this galley', + "Errors found rendering this galley", ) - elif galley.type == 'epub': - template = 'proofing/preview/epub.html' + elif galley.type == "epub": + template = "proofing/preview/epub.html" else: - template = 'proofing/preview/embedded.html' + template = "proofing/preview/embedded.html" context = { - 'proofing_task': proofing_task, - 'galley': galley, - 'article': proofing_task.round.assignment.article, - 'article_content': article_content, + "proofing_task": proofing_task, + "galley": galley, + "article": proofing_task.round.assignment.article, + "article_content": article_content, } return render(request, template, context) @@ -586,10 +599,10 @@ def request_typesetting_changes(request, article_id, proofing_task_id): files = logic.get_files_from_post(request) if not user: - form.add_error(None, 'You must select a typesetter.') + form.add_error(None, "You must select a typesetter.") if not galleys: - form.add_error(None, 'You must select at least one galley.') + form.add_error(None, "You must select at least one galley.") if form.is_valid(): typeset_task = form.save( @@ -603,22 +616,26 @@ def request_typesetting_changes(request, article_id, proofing_task_id): return redirect( reverse( - 'notify_typesetter_changes', - kwargs={'article_id': article.pk, - 'proofing_task_id': proofing_task.pk, - 'typeset_task_id': typeset_task.pk}, + "notify_typesetter_changes", + kwargs={ + "article_id": article.pk, + "proofing_task_id": proofing_task.pk, + "typeset_task_id": typeset_task.pk, + }, ) ) - template = 'proofing/request_typesetting_changes.html' + template = "proofing/request_typesetting_changes.html" context = { - 'article': article, - 'proofing_task': proofing_task, - 'typesetters': core_models.AccountRole.objects.filter(role__slug='typesetter', journal=article.journal), - 'user': user if request.POST else None, - 'galleys': galleys if request.POST else None, - 'form': form, - 'comments': comments, + "article": article, + "proofing_task": proofing_task, + "typesetters": core_models.AccountRole.objects.filter( + role__slug="typesetter", journal=article.journal + ), + "user": user if request.POST else None, + "galleys": galleys if request.POST else None, + "form": form, + "comments": comments, } return render(request, template, context) @@ -641,31 +658,36 @@ def notify_typesetter_changes(request, article_id, proofing_task_id, typeset_tas ) proofing_task = get_object_or_404(models.ProofingTask, pk=proofing_task_id) typeset_task = get_object_or_404(models.TypesetterProofingTask, pk=typeset_task_id) - notification_email_content = logic.get_notify_typesetter(request, article, proofing_task, typeset_task) + notification_email_content = logic.get_notify_typesetter( + request, article, proofing_task, typeset_task + ) if request.POST: - notification_email_content = request.POST.get('content_email', None) + notification_email_content = request.POST.get("content_email", None) typeset_task.notified = True typeset_task.save() - kwargs = {'request': request, - 'typeset_task': typeset_task, - 'article': article, - 'user_content_message': notification_email_content, - 'skip': True if 'skip' in request.POST else False, - } + kwargs = { + "request": request, + "typeset_task": typeset_task, + "article": article, + "user_content_message": notification_email_content, + "skip": True if "skip" in request.POST else False, + } - event_logic.Events.raise_event(event_logic.Events.ON_PROOFING_TYPESET_CHANGES_REQUEST, - task_object=article, - **kwargs) - return redirect(reverse('proofing_article', kwargs={'article_id': article.pk})) + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFING_TYPESET_CHANGES_REQUEST, + task_object=article, + **kwargs, + ) + return redirect(reverse("proofing_article", kwargs={"article_id": article.pk})) - template = 'proofing/notify_typesetter_changes.html' + template = "proofing/notify_typesetter_changes.html" context = { - 'article': article, - 'proofing_task': proofing_task, - 'typeset_task': typeset_task, - 'notification_email_content': notification_email_content, + "article": article, + "proofing_task": proofing_task, + "typeset_task": typeset_task, + "notification_email_content": notification_email_content, } return render(request, template, context) @@ -683,7 +705,6 @@ def typesetting_corrections(request, typeset_task_id): models.TypesetterProofingTask.active_objects, pk=typeset_task_id, completed__isnull=True, - ) article = typeset_task.proofing_task.round.assignment.article form = forms.CompleteCorrections(instance=typeset_task) @@ -696,21 +717,25 @@ def typesetting_corrections(request, typeset_task_id): typeset_task.completed = timezone.now() typeset_task.save() - kwargs = {'article': article, 'typeset_task': typeset_task, 'request': request} + kwargs = { + "article": article, + "typeset_task": typeset_task, + "request": request, + } event_logic.Events.raise_event( event_logic.Events.ON_CORRECTIONS_COMPLETE, task_object=article, **kwargs, ) - messages.add_message(request, messages.INFO, 'Corrections task complete') - return redirect(reverse('proofing_correction_requests')) + messages.add_message(request, messages.INFO, "Corrections task complete") + return redirect(reverse("proofing_correction_requests")) - template = 'proofing/typesetting/typesetting_corrections.html' + template = "proofing/typesetting/typesetting_corrections.html" context = { - 'typeset_task': typeset_task, - 'article': article, - 'form': form, + "typeset_task": typeset_task, + "article": article, + "form": form, } return render(request, template, context) @@ -735,25 +760,29 @@ def acknowledge(request, article_id, model_name, model_pk): text = logic.get_ack_message(request, article, model_name, model_object) if request.POST: - message = request.POST.get('content_email', None) - kwargs = {'request': request, - 'article': article, - 'user_message': message, - 'model_object': model_object, - 'model_name': model_name, - 'skip': True if 'skip' in request.POST else False} - event_logic.Events.raise_event(event_logic.Events.ON_PROOFING_ACK, task_object=article, **kwargs) + message = request.POST.get("content_email", None) + kwargs = { + "request": request, + "article": article, + "user_message": message, + "model_object": model_object, + "model_name": model_name, + "skip": True if "skip" in request.POST else False, + } + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFING_ACK, task_object=article, **kwargs + ) model_object.acknowledged = timezone.now() model_object.save() - return redirect(reverse('proofing_article', kwargs={'article_id': article.pk})) + return redirect(reverse("proofing_article", kwargs={"article_id": article.pk})) - template = 'proofing/acknowledge.html' + template = "proofing/acknowledge.html" context = { - 'model': model, - 'model_object': model_object, - 'model_name': model_name, - 'text': text, - 'article': article, + "model": model, + "model_object": model_object, + "model_name": model_name, + "text": text, + "article": article, } return render(request, template, context) @@ -776,29 +805,44 @@ def complete_proofing(request, article_id): message = logic.get_complete_proofing_message(request, article) if request.POST: - message = request.POST.get('content_email') + message = request.POST.get("content_email") article.stage = submission_models.STAGE_READY_FOR_PUBLICATION article.proofingassignment.completed = timezone.now() article.save() journal_models.FixedPubCheckItems.objects.get_or_create(article=article) - kwargs = {'request': request, 'article': article, 'user_message': message, - 'skip': True if 'skip' in request.POST else False} - event_logic.Events.raise_event(event_logic.Events.ON_PROOFING_COMPLETE, task_object=article, **kwargs) + kwargs = { + "request": request, + "article": article, + "user_message": message, + "skip": True if "skip" in request.POST else False, + } + event_logic.Events.raise_event( + event_logic.Events.ON_PROOFING_COMPLETE, task_object=article, **kwargs + ) - if request.journal.element_in_workflow(element_name='proofing'): - workflow_kwargs = {'handshake_url': 'proofing_list', 'request': request, 'article': article, - 'switch_stage': True} - return event_logic.Events.raise_event(event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, task_object=article, - **workflow_kwargs) + if request.journal.element_in_workflow(element_name="proofing"): + workflow_kwargs = { + "handshake_url": "proofing_list", + "request": request, + "article": article, + "switch_stage": True, + } + return event_logic.Events.raise_event( + event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, + task_object=article, + **workflow_kwargs, + ) else: - return redirect(reverse('publish_article', kwargs={'article_id': article.pk})) + return redirect( + reverse("publish_article", kwargs={"article_id": article.pk}) + ) - template = 'proofing/complete_proofing.html' + template = "proofing/complete_proofing.html" context = { - 'article': article, - 'message': message, + "article": article, + "message": message, } return render(request, template, context) @@ -819,14 +863,18 @@ def new_note(request, proofing_task_id, galley_id): if request.user.is_staff: proofing_task = get_object_or_404(models.ProofingTask, pk=proofing_task_id) else: - proofing_task = get_object_or_404(models.ProofingTask, - (Q(proofreader=request.user) | - Q(round__assignment__proofing_manager=request.user)), - pk=proofing_task_id) + proofing_task = get_object_or_404( + models.ProofingTask, + ( + Q(proofreader=request.user) + | Q(round__assignment__proofing_manager=request.user) + ), + pk=proofing_task_id, + ) galley = get_object_or_404(core_models.Galley, pk=galley_id) if request.POST: - note = request.POST.get('note') + note = request.POST.get("note") note = models.Note.objects.create( galley=galley, creator=request.user, @@ -835,13 +883,16 @@ def new_note(request, proofing_task_id, galley_id): proofing_task.notes.add(note) - return_dict = {'id': note.pk, 'note': note.text, 'initials': note.creator.initials(), - 'date_time': note.date_time.strftime("%Y-%m-%d %H:%i"), - 'html': logic.create_html_snippet(note, proofing_task, galley)} + return_dict = { + "id": note.pk, + "note": note.text, + "initials": note.creator.initials(), + "date_time": note.date_time.strftime("%Y-%m-%d %H:%i"), + "html": logic.create_html_snippet(note, proofing_task, galley), + } else: - - return_dict = {'error': 'This request must be made with POST'} + return_dict = {"error": "This request must be made with POST"} return HttpResponse(json.dumps(return_dict), content_type="application/json") @@ -858,20 +909,24 @@ def delete_note(request, proofing_task_id, galley_id): if request.user.is_staff: get_object_or_404(models.ProofingTask, pk=proofing_task_id) else: - get_object_or_404(models.ProofingTask, - (Q(proofreader=request.user) | - Q(round__assignment__proofing_manager=request.user)), - pk=proofing_task_id) + get_object_or_404( + models.ProofingTask, + ( + Q(proofreader=request.user) + | Q(round__assignment__proofing_manager=request.user) + ), + pk=proofing_task_id, + ) if request.POST: - note_id = request.POST.get('note_id') + note_id = request.POST.get("note_id") note = get_object_or_404(models.Note, galley__pk=galley_id, pk=note_id) note.delete() - return_dict = {'id': note.pk, 'deleted': True} + return_dict = {"id": note.pk, "deleted": True} else: - return_dict = {'deleted': False} + return_dict = {"deleted": False} return HttpResponse(json.dumps(return_dict), content_type="application/json") @@ -887,11 +942,15 @@ def proofing_download(request, proofing_task_id, file_id): if file in proofing_task.galley_files(): return files.serve_file(request, file, proofing_task.round.assignment.article) else: - messages.add_message(request, messages.WARNING, 'Requested file is not a galley for proofing') - return redirect(request.META.get('HTTP_REFERER')) + messages.add_message( + request, messages.WARNING, "Requested file is not a galley for proofing" + ) + return redirect(request.META.get("HTTP_REFERER")) @proofreader_for_article_required def preview_figure(request, proofing_task_id, galley_id, file_name): - galley = get_object_or_404(core_models.Galley, pk=galley_id, article__journal=request.journal) + galley = get_object_or_404( + core_models.Galley, pk=galley_id, article__journal=request.journal + ) return article_figure(request, galley.article.pk, galley_id, file_name) diff --git a/src/reports/apps.py b/src/reports/apps.py index 62cb66903e..f5fbe30b47 100755 --- a/src/reports/apps.py +++ b/src/reports/apps.py @@ -2,4 +2,4 @@ class ReportsConfig(AppConfig): - name = 'reports' + name = "reports" diff --git a/src/reports/urls.py b/src/reports/urls.py index 49348e562d..2f787192ec 100755 --- a/src/reports/urls.py +++ b/src/reports/urls.py @@ -10,10 +10,8 @@ urlpatterns = [ - # Editor URLs - re_path(r'^$', views.index, name='reports_index'), - re_path(r'^metrics/$', views.metrics, name='reports_metrics'), - re_path(r'^doiresolution/$', views.dois, name='reports_dois'), - + re_path(r"^$", views.index, name="reports_index"), + re_path(r"^metrics/$", views.metrics, name="reports_metrics"), + re_path(r"^doiresolution/$", views.dois, name="reports_dois"), ] diff --git a/src/reports/views.py b/src/reports/views.py index 61bfb152ec..4ea9cae524 100755 --- a/src/reports/views.py +++ b/src/reports/views.py @@ -21,7 +21,7 @@ def index(request): :param request: HttpRequest object :return: HttpResponse """ - template = 'reports/index.html' + template = "reports/index.html" context = {} return render(request, template, context) @@ -34,16 +34,17 @@ def metrics(request): :param request: HttpRequest object :return: HttpResponse """ - articles = models.Article.objects.filter(journal=request.journal, - stage=models.STAGE_PUBLISHED) + articles = models.Article.objects.filter( + journal=request.journal, stage=models.STAGE_PUBLISHED + ) total_views, total_downs = logic.get_view_and_download_totals(articles) - template = 'reports/metrics.html' + template = "reports/metrics.html" context = { - 'articles': articles, - 'total_views': total_views, - 'total_downs': total_downs, + "articles": articles, + "total_views": total_views, + "total_downs": total_downs, } return render(request, template, context) @@ -56,15 +57,17 @@ def dois(request): :param request: HttpRequest object :return: HttpResponse object """ - broken_dois = ident_models.BrokenDOI.objects.filter(article__journal=request.journal) + broken_dois = ident_models.BrokenDOI.objects.filter( + article__journal=request.journal + ) - if request.POST and 'run' in request.POST: - call_command('doi_check') - return redirect(reverse('reports_dois', request.journal.code)) + if request.POST and "run" in request.POST: + call_command("doi_check") + return redirect(reverse("reports_dois", request.journal.code)) - template = 'reports/dois.html' + template = "reports/dois.html" context = { - 'broken_dois': broken_dois, + "broken_dois": broken_dois, } return render(request, template, context) diff --git a/src/repository/admin.py b/src/repository/admin.py index 676d38840b..917961fd0c 100755 --- a/src/repository/admin.py +++ b/src/repository/admin.py @@ -12,12 +12,14 @@ class RepositoryAdmin(SimpleHistoryAdmin): - list_display = ('pk', 'short_name', 'name', 'live') - list_display_links = ('short_name', 'name') - list_filter = ('live',) - search_fields = ('short_name', 'name',) - raw_id_fields = ('managers', 'homepage_preprints', - 'active_licenses') + list_display = ("pk", "short_name", "name", "live") + list_display_links = ("short_name", "name") + list_filter = ("live",) + search_fields = ( + "short_name", + "name", + ) + raw_id_fields = ("managers", "homepage_preprints", "active_licenses") inlines = [ admin_utils.RepositoryRoleInline, @@ -25,52 +27,100 @@ class RepositoryAdmin(SimpleHistoryAdmin): class RepositoryRoleAdmin(admin.ModelAdmin): - list_display = ('pk', 'repository', 'user', 'role') - list_filter = ('repository', 'role') - search_fields = ('repository__name', 'repository__short_name', - 'user__email', 'user__first_name', - 'user__last_name', 'role__slug', - 'role__name') - raw_id_fields = ('repository', 'user', 'role') + list_display = ("pk", "repository", "user", "role") + list_filter = ("repository", "role") + search_fields = ( + "repository__name", + "repository__short_name", + "user__email", + "user__first_name", + "user__last_name", + "role__slug", + "role__name", + ) + raw_id_fields = ("repository", "user", "role") class RepositoryFieldAdmin(admin.ModelAdmin): - list_display = ('name', 'input_type', 'required', 'display', - 'dc_metadata_type', 'repository') - list_filter = ('repository__short_name', 'input_type', - 'required', 'display', 'dc_metadata_type') - search_fields = ('name', 'help_text', 'dc_metadata_type', 'choices') + list_display = ( + "name", + "input_type", + "required", + "display", + "dc_metadata_type", + "repository", + ) + list_filter = ( + "repository__short_name", + "input_type", + "required", + "display", + "dc_metadata_type", + ) + search_fields = ("name", "help_text", "dc_metadata_type", "choices") class RepositoryFieldAnswerAdmin(admin.ModelAdmin): - list_display = ('_answer', 'field', 'preprint', '_repository') - list_filter = ('field__repository__short_name', 'field') - search_fields = ('answer', 'preprint__title', 'preprint__pk') - raw_id_fields = ('field',) + list_display = ("_answer", "field", "preprint", "_repository") + list_filter = ("field__repository__short_name", "field") + search_fields = ("answer", "preprint__title", "preprint__pk") + raw_id_fields = ("field",) def _answer(self, obj): - return truncatewords_html(obj.answer, 10) if obj else '' + return truncatewords_html(obj.answer, 10) if obj else "" def _repository(self, obj): - return obj.field.repository if obj else '' + return obj.field.repository if obj else "" class PreprintAdmin(admin.ModelAdmin): - list_display = ('pk', 'title', 'owner', 'repository', - 'date_submitted', 'doi', 'current_version') - list_display_links = ('pk', 'title') - list_filter = ('repository__short_name', 'date_started', - 'date_submitted', 'date_accepted', 'date_declined', - 'date_published', 'date_updated', 'current_step') - raw_id_fields = ('repository', 'owner', 'subject', - 'article', 'submission_file', 'license') - search_fields = ('pk', 'title', 'owner__email', 'owner__orcid', - 'owner__first_name', 'owner__last_name', 'abstract', - 'submission_file__original_filename', 'subject__name', - 'comments_editor', 'doi', 'preprint_doi', - 'preprint_decline_note', 'article__pk', 'article__title') - filter_horizontal = ('keywords',) - date_hierarchy = ('date_submitted') + list_display = ( + "pk", + "title", + "owner", + "repository", + "date_submitted", + "doi", + "current_version", + ) + list_display_links = ("pk", "title") + list_filter = ( + "repository__short_name", + "date_started", + "date_submitted", + "date_accepted", + "date_declined", + "date_published", + "date_updated", + "current_step", + ) + raw_id_fields = ( + "repository", + "owner", + "subject", + "article", + "submission_file", + "license", + ) + search_fields = ( + "pk", + "title", + "owner__email", + "owner__orcid", + "owner__first_name", + "owner__last_name", + "abstract", + "submission_file__original_filename", + "subject__name", + "comments_editor", + "doi", + "preprint_doi", + "preprint_decline_note", + "article__pk", + "article__title", + ) + filter_horizontal = ("keywords",) + date_hierarchy = "date_submitted" inlines = [ admin_utils.PreprintAuthorInline, @@ -88,114 +138,197 @@ class PreprintAdmin(admin.ModelAdmin): class KeywordPreprintAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('keyword', '_preprint', 'order', '_repository') - list_filter = ('preprint__repository__short_name',) - raw_id_fields = ('keyword', 'preprint') - search_fields = ('keyword__word', 'preprint__pk', 'preprint__title',) + list_display = ("keyword", "_preprint", "order", "_repository") + list_filter = ("preprint__repository__short_name",) + raw_id_fields = ("keyword", "preprint") + search_fields = ( + "keyword__word", + "preprint__pk", + "preprint__title", + ) class PreprintFileAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('_preprint', 'file', 'original_filename', 'uploaded', - '_repository') - list_filter = ('preprint__repository__short_name', 'uploaded', 'mime_type') - raw_id_fields = ('preprint',) - search_fields = ('preprint__pk', 'preprint__title', 'original_filename') - date_hierarchy = ('uploaded') + list_display = ("_preprint", "file", "original_filename", "uploaded", "_repository") + list_filter = ("preprint__repository__short_name", "uploaded", "mime_type") + raw_id_fields = ("preprint",) + search_fields = ("preprint__pk", "preprint__title", "original_filename") + date_hierarchy = "uploaded" class PreprintSupplementaryFileAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('_preprint', 'url', 'label', 'order', '_repository') - list_filter = ('preprint__repository__short_name',) - search_fields = ('preprint__pk', 'preprint__title', 'url', 'label') + list_display = ("_preprint", "url", "label", "order", "_repository") + list_filter = ("preprint__repository__short_name",) + search_fields = ("preprint__pk", "preprint__title", "url", "label") class PreprintAccessAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('_preprint', 'file', 'accessed', 'access_type', 'country', - '_repository') - list_filter = ('preprint__repository__short_name', 'accessed', - 'country') - search_fields = ('preprint__pk', 'preprint__title', 'identifier', - 'file__original_filename') - raw_id_fields = ('preprint',) + list_display = ( + "_preprint", + "file", + "accessed", + "access_type", + "country", + "_repository", + ) + list_filter = ("preprint__repository__short_name", "accessed", "country") + search_fields = ( + "preprint__pk", + "preprint__title", + "identifier", + "file__original_filename", + ) + raw_id_fields = ("preprint",) save_as = True class PreprintAuthorAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('pk', '_preprint', 'account', 'order', '_repository') - list_filter = ('preprint__repository__short_name', 'account', 'preprint') - raw_id_fields = ('preprint', 'account') - search_fields = ('preprint__pk', 'preprint__title', - 'account__email', 'account__orcid', - 'account__first_name', 'account__last_name') + list_display = ("pk", "_preprint", "account", "order", "_repository") + list_filter = ("preprint__repository__short_name", "account", "preprint") + raw_id_fields = ("preprint", "account") + search_fields = ( + "preprint__pk", + "preprint__title", + "account__email", + "account__orcid", + "account__first_name", + "account__last_name", + ) class PreprintVersionAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('pk', '_preprint', 'title', 'version', 'date_time', - '_repository') - list_filter = ('preprint__repository__short_name',) - raw_id_fields = ('preprint', 'file') - date_hierarchy = ('date_time') - search_fields = ('preprint__title', 'title', 'abstract', - 'file__original_filename', 'published_doi') + list_display = ("pk", "_preprint", "title", "version", "date_time", "_repository") + list_filter = ("preprint__repository__short_name",) + raw_id_fields = ("preprint", "file") + date_hierarchy = "date_time" + search_fields = ( + "preprint__title", + "title", + "abstract", + "file__original_filename", + "published_doi", + ) class CommentAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('_body', '_preprint', 'author', '_repository', - 'date_time', 'is_reviewed', 'is_public') - list_filter = ('preprint__repository__short_name', - 'date_time', 'is_reviewed', 'is_public') - search_fields = ('body', 'preprint__title', 'author__email', - 'author__first_name', 'author__last_name',) - raw_id_fields = ('preprint', 'author', 'reply_to',) - date_hierarchy = ('date_time') + list_display = ( + "_body", + "_preprint", + "author", + "_repository", + "date_time", + "is_reviewed", + "is_public", + ) + list_filter = ( + "preprint__repository__short_name", + "date_time", + "is_reviewed", + "is_public", + ) + search_fields = ( + "body", + "preprint__title", + "author__email", + "author__first_name", + "author__last_name", + ) + raw_id_fields = ( + "preprint", + "author", + "reply_to", + ) + date_hierarchy = "date_time" inlines = [ admin_utils.CommentInline, ] def _body(self, obj): - return truncatewords_html(obj.body, 8) if obj else '' + return truncatewords_html(obj.body, 8) if obj else "" class SubjectAdmin(admin.ModelAdmin): - list_display = ('name', 'slug', 'repository', 'enabled', 'parent') - list_filter = ('repository__short_name', 'enabled') - search_fields = ('name', 'slug') - raw_id_fields = ('editors',) + list_display = ("name", "slug", "repository", "enabled", "parent") + list_filter = ("repository__short_name", "enabled") + search_fields = ("name", "slug") + raw_id_fields = ("editors",) class VersionQueueAdmin(admin.ModelAdmin): - list_display = ('pk', 'preprint', 'file', 'update_type', - 'date_submitted', 'approved', 'date_decision') - list_filter = ('preprint__repository__short_name', 'update_type', - 'approved', 'date_submitted', 'date_decision') - raw_id_fields = ('preprint', 'file',) - search_fields = ('preprint__title', 'file__original_filename', - 'title', 'abstract', 'published_doi') - date_hierarchy = ('date_submitted') + list_display = ( + "pk", + "preprint", + "file", + "update_type", + "date_submitted", + "approved", + "date_decision", + ) + list_filter = ( + "preprint__repository__short_name", + "update_type", + "approved", + "date_submitted", + "date_decision", + ) + raw_id_fields = ( + "preprint", + "file", + ) + search_fields = ( + "preprint__title", + "file__original_filename", + "title", + "abstract", + "published_doi", + ) + date_hierarchy = "date_submitted" class ReviewAdmin(admin_utils.PreprintFKModelAdmin): - list_display = ('pk', 'reviewer', '_preprint', 'manager', '_repository', - 'date_assigned') - list_display_links = ('pk', 'reviewer') - list_filter = ('preprint__repository__short_name', 'status', - 'anonymous', 'notification_sent', 'date_assigned', - 'date_accepted', 'date_due', 'date_completed') - raw_id_fields = ('preprint', 'manager', 'reviewer', 'comment') - search_fields = ('preprint__pk', 'preprint__title', - 'reviewer__email', 'reviewer__first_name', - 'reviewer__last_name', 'manager__email', - 'manager__first_name', 'manager__last_name') - date_hierarchy = ('date_assigned') + list_display = ( + "pk", + "reviewer", + "_preprint", + "manager", + "_repository", + "date_assigned", + ) + list_display_links = ("pk", "reviewer") + list_filter = ( + "preprint__repository__short_name", + "status", + "anonymous", + "notification_sent", + "date_assigned", + "date_accepted", + "date_due", + "date_completed", + ) + raw_id_fields = ("preprint", "manager", "reviewer", "comment") + search_fields = ( + "preprint__pk", + "preprint__title", + "reviewer__email", + "reviewer__first_name", + "reviewer__last_name", + "manager__email", + "manager__first_name", + "manager__last_name", + ) + date_hierarchy = "date_assigned" class ReviewRecommendationAdmin(admin.ModelAdmin): - list_display = ('pk', 'name', 'repository') - list_display_links = ('pk', 'name',) - list_filter = ('repository',) - raw_id_fields = ('repository',) - search_fields = ('name',) + list_display = ("pk", "name", "repository") + list_display_links = ( + "pk", + "name", + ) + list_filter = ("repository",) + raw_id_fields = ("repository",) + search_fields = ("name",) admin_list = [ diff --git a/src/repository/apps.py b/src/repository/apps.py index ba92fc1e19..3bf53541f4 100755 --- a/src/repository/apps.py +++ b/src/repository/apps.py @@ -7,4 +7,4 @@ class PreprintConfig(AppConfig): - name = 'repository' + name = "repository" diff --git a/src/repository/forms.py b/src/repository/forms.py index b56036da59..f2cea43357 100755 --- a/src/repository/forms.py +++ b/src/repository/forms.py @@ -25,91 +25,93 @@ class PreprintInfo(utils_forms.KeywordModelForm): subject = forms.ModelMultipleChoiceField( required=True, queryset=models.Subject.objects.none(), - widget=forms.SelectMultiple(attrs={'multiple': ''}), + widget=forms.SelectMultiple(attrs={"multiple": ""}), ) class Meta: model = models.Preprint fields = ( - 'title', - 'abstract', - 'license', - 'comments_editor', - 'subject', - 'doi', + "title", + "abstract", + "license", + "comments_editor", + "subject", + "doi", ) widgets = { - 'title': forms.TextInput(attrs={'placeholder': _('Title')}), - 'abstract': forms.Textarea( - attrs={ - 'placeholder': _('Enter your article\'s abstract here') - } + "title": forms.TextInput(attrs={"placeholder": _("Title")}), + "abstract": forms.Textarea( + attrs={"placeholder": _("Enter your article's abstract here")} ), } def __init__(self, *args, **kwargs): - self.request = kwargs.pop('request') - self.admin = kwargs.pop('admin', False) + self.request = kwargs.pop("request") + self.admin = kwargs.pop("admin", False) elements = self.request.repository.additional_submission_fields() super(PreprintInfo, self).__init__(*args, **kwargs) if self.admin: - self.fields.pop('submission_agreement') - self.fields.pop('comments_editor') + self.fields.pop("submission_agreement") + self.fields.pop("comments_editor") # If using this form and there is an instance then this has # previously been checked as it is required. if self.instance: - self.fields['submission_agreement'].initial = True + self.fields["submission_agreement"].initial = True - self.fields['subject'].queryset = models.Subject.objects.filter( + self.fields["subject"].queryset = models.Subject.objects.filter( enabled=True, repository=self.request.repository, ) if self.admin: - self.fields['license'].queryset = submission_models.Licence.objects.filter( + self.fields["license"].queryset = submission_models.Licence.objects.filter( journal__isnull=True, ) else: - self.fields['license'].queryset = self.request.repository.active_licenses.all() - self.fields['license'].required = True + self.fields[ + "license" + ].queryset = self.request.repository.active_licenses.all() + self.fields["license"].required = True if elements: for element in elements: - if element.input_type == 'text': + if element.input_type == "text": self.fields[element.name] = forms.CharField( - widget=forms.TextInput(), - required=element.required) - elif element.input_type == 'textarea': + widget=forms.TextInput(), required=element.required + ) + elif element.input_type == "textarea": self.fields[element.name] = forms.CharField( widget=forms.Textarea, required=element.required, ) - elif element.input_type == 'date': + elif element.input_type == "date": self.fields[element.name] = forms.CharField( widget=forms.DateInput( attrs={ - 'class': 'datepicker', + "class": "datepicker", } ), - required=element.required) - elif element.input_type == 'select': + required=element.required, + ) + elif element.input_type == "select": choices = render_choices(element.choices) self.fields[element.name] = forms.ChoiceField( widget=forms.Select(), choices=choices, required=element.required, ) - elif element.input_type == 'email': + elif element.input_type == "email": self.fields[element.name] = forms.EmailField( widget=forms.TextInput(), required=element.required, ) - elif element.input_type == 'checkbox': + elif element.input_type == "checkbox": self.fields[element.name] = forms.BooleanField( - widget=forms.CheckboxInput(attrs={'is_checkbox': True}), - required=element.required) - elif element.input_type == 'number': + widget=forms.CheckboxInput(attrs={"is_checkbox": True}), + required=element.required, + ) + elif element.input_type == "number": self.fields[element.name] = forms.IntegerField( required=element.required, ) @@ -119,13 +121,17 @@ def __init__(self, *args, **kwargs): required=element.required, ) - if element.input_type == 'date': - self.fields[element.name].help_text = 'Use ISO 8601 Date Format YYYY-MM-DD. {}'.format(element.help_text) + if element.input_type == "date": + self.fields[ + element.name + ].help_text = "Use ISO 8601 Date Format YYYY-MM-DD. {}".format( + element.help_text + ) else: self.fields[element.name].help_text = element.help_text self.fields[element.name].label = element.name - preprint = kwargs['instance'] + preprint = kwargs["instance"] if preprint: try: check_for_answer = models.RepositoryFieldAnswer.objects.get( @@ -173,12 +179,12 @@ def save(self, commit=True): return preprint def clean_doi(self): - doi_string = self.cleaned_data.get('doi') + doi_string = self.cleaned_data.get("doi") if doi_string and not URL_DOI_RE.match(doi_string): self.add_error( - 'doi', - 'DOIs should be in the following format: https://doi.org/10.XXX/XXXXX' + "doi", + "DOIs should be in the following format: https://doi.org/10.XXX/XXXXX", ) return doi_string @@ -187,10 +193,13 @@ def clean_doi(self): class PreprintSupplementaryFileForm(forms.ModelForm): class Meta: model = models.PreprintSupplementaryFile - fields = ('label', 'url',) + fields = ( + "label", + "url", + ) def __init__(self, *args, **kwargs): - self.preprint = kwargs.pop('preprint') + self.preprint = kwargs.pop("preprint") super(PreprintSupplementaryFileForm, self).__init__(*args, **kwargs) def save(self, commit=True): @@ -211,60 +220,62 @@ class AuthorForm(forms.Form): affiliation = forms.CharField(max_length=200, required=False) def __init__(self, *args, **kwargs): - self.instance = kwargs.pop('instance') - self.request = kwargs.pop('request') - self.preprint = kwargs.pop('preprint') + self.instance = kwargs.pop("instance") + self.request = kwargs.pop("request") + self.preprint = kwargs.pop("preprint") super(AuthorForm, self).__init__(*args, **kwargs) if self.instance: - self.fields['email_address'].initial = self.instance.account.email - self.fields['first_name'].initial = self.instance.account.first_name - self.fields['middle_name'].initial = self.instance.account.middle_name - self.fields['last_name'].initial = self.instance.account.last_name - self.fields['affiliation'].initial = self.instance.affiliation or self.instance.account.institution + self.fields["email_address"].initial = self.instance.account.email + self.fields["first_name"].initial = self.instance.account.first_name + self.fields["middle_name"].initial = self.instance.account.middle_name + self.fields["last_name"].initial = self.instance.account.last_name + self.fields["affiliation"].initial = ( + self.instance.affiliation or self.instance.account.institution + ) def save(self): cleaned_data = self.cleaned_data if self.instance: account = self.instance.account - account.email = cleaned_data.get('email_address') - account.first_name = cleaned_data.get('first_name') - account.middle_name = cleaned_data.get('middle_name') - account.last_name = cleaned_data.get('last_name') - self.instance.affiliation = cleaned_data.get('affiliation') + account.email = cleaned_data.get("email_address") + account.first_name = cleaned_data.get("first_name") + account.middle_name = cleaned_data.get("middle_name") + account.last_name = cleaned_data.get("last_name") + self.instance.affiliation = cleaned_data.get("affiliation") account.save() self.instance.save() return self.instance else: account, ac = core_models.Account.objects.get_or_create( - email=cleaned_data.get('email_address'), + email=cleaned_data.get("email_address"), defaults={ - 'first_name': cleaned_data.get('first_name'), - 'middle_name': cleaned_data.get('middle_name'), - 'last_name': cleaned_data.get('last_name'), - } + "first_name": cleaned_data.get("first_name"), + "middle_name": cleaned_data.get("middle_name"), + "last_name": cleaned_data.get("last_name"), + }, ) preprint_author, pc = models.PreprintAuthor.objects.get_or_create( account=account, preprint=self.preprint, defaults={ - 'affiliation': cleaned_data.get('affiliation'), - 'order': self.preprint.next_author_order() - } + "affiliation": cleaned_data.get("affiliation"), + "order": self.preprint.next_author_order(), + }, ) if not ac: messages.add_message( self.request, messages.WARNING, - 'A user with this email address was found. They have been added.' + "A user with this email address was found. They have been added.", ) else: messages.add_message( self.request, messages.SUCCESS, - 'User added as Author.', + "User added as Author.", ) return preprint_author @@ -273,11 +284,11 @@ def save(self): class CommentForm(forms.ModelForm): class Meta: model = models.Comment - fields = ('body',) + fields = ("body",) def __init__(self, *args, **kwargs): - self.preprint = kwargs.pop('preprint', None) - self.author = kwargs.pop('author', None) + self.preprint = kwargs.pop("preprint", None) + self.author = kwargs.pop("author", None) super(CommentForm, self).__init__(*args, **kwargs) def save(self, commit=True): @@ -295,25 +306,25 @@ class SettingsForm(forms.ModelForm): class Meta: model = press_models.Press fields = ( - 'preprints_about', - 'preprint_start', - 'preprint_submission', - 'preprint_publication', - 'preprint_decline', - 'preprint_pdf_only', + "preprints_about", + "preprint_start", + "preprint_submission", + "preprint_publication", + "preprint_decline", + "preprint_pdf_only", ) widgets = { - 'preprints_about': TinyMCE, - 'preprint_start': TinyMCE, - 'preprint_submission': TinyMCE, - 'preprint_publication': TinyMCE, - 'preprint_decline': TinyMCE, + "preprints_about": TinyMCE, + "preprint_start": TinyMCE, + "preprint_submission": TinyMCE, + "preprint_publication": TinyMCE, + "preprint_decline": TinyMCE, } def __init__(self, *args, **kwargs): super(SettingsForm, self).__init__(*args, **kwargs) - if 'instance' in kwargs: - press = kwargs['instance'] + if "instance" in kwargs: + press = kwargs["instance"] settings = press_models.PressSetting.objects.filter(press=press) for setting in settings: @@ -335,7 +346,7 @@ def save(self, commit=True): for setting in settings: if setting.is_boolean: - setting.value = 'On' if self.cleaned_data[setting.name] else '' + setting.value = "On" if self.cleaned_data[setting.name] else "" else: setting.value = self.cleaned_data[setting.name] setting.save() @@ -344,12 +355,12 @@ def save(self, commit=True): class SubjectForm(forms.ModelForm): class Meta: model = models.Subject - exclude = ('repository', 'slug') + exclude = ("repository", "slug") def __init__(self, *args, **kwargs): - self.repository = kwargs.pop('repository') + self.repository = kwargs.pop("repository") super(SubjectForm, self).__init__(*args, **kwargs) - self.fields['parent'].queryset = models.Subject.objects.filter( + self.fields["parent"].queryset = models.Subject.objects.filter( repository=self.repository, ) @@ -367,20 +378,20 @@ def save(self, commit=True): class ActiveLicenseForm(forms.ModelForm): class Meta: model = models.Repository - fields = ('active_licenses',) + fields = ("active_licenses",) widgets = { - 'active_licenses': forms.CheckboxSelectMultiple, + "active_licenses": forms.CheckboxSelectMultiple, } labels = { - 'active_licenses': 'Select the licenses that authors can pick from during submission', + "active_licenses": "Select the licenses that authors can pick from during submission", } def __init__(self, *args, **kwargs): super(ActiveLicenseForm, self).__init__(*args, **kwargs) - self.fields['active_licenses'].queryset = submission_models.Licence.objects.filter( - journal=None - ) - self.fields['active_licenses'].label_from_instance = self.label_from_instance + self.fields[ + "active_licenses" + ].queryset = submission_models.Licence.objects.filter(journal=None) + self.fields["active_licenses"].label_from_instance = self.label_from_instance @staticmethod def label_from_instance(obj): @@ -390,10 +401,10 @@ def label_from_instance(obj): class FileForm(forms.ModelForm): class Meta: model = models.PreprintFile - fields = ('file',) + fields = ("file",) def __init__(self, *args, **kwargs): - self.preprint = kwargs.pop('preprint') + self.preprint = kwargs.pop("preprint") super(FileForm, self).__init__(*args, **kwargs) def save(self, commit=True): @@ -412,14 +423,14 @@ def save(self, commit=True): class VersionForm(forms.ModelForm): class Meta: model = models.VersionQueue - fields = ('title', 'abstract', 'published_doi') + fields = ("title", "abstract", "published_doi") def __init__(self, *args, **kwargs): - self.preprint = kwargs.pop('preprint') + self.preprint = kwargs.pop("preprint") super(VersionForm, self).__init__(*args, **kwargs) - self.fields['title'].initial = self.preprint.title - self.fields['abstract'].initial = self.preprint.abstract - self.fields['published_doi'].initial = self.preprint.doi + self.fields["title"].initial = self.preprint.title + self.fields["abstract"].initial = self.preprint.abstract + self.fields["published_doi"].initial = self.preprint.doi def save(self, commit=True): version = super(VersionForm, self).save(commit=False) @@ -431,12 +442,12 @@ def save(self, commit=True): return version def clean_published_doi(self): - doi_string = self.cleaned_data.get('published_doi') + doi_string = self.cleaned_data.get("published_doi") if doi_string and not URL_DOI_RE.match(doi_string): self.add_error( - 'published_doi', - 'DOIs should be in the following format: https://doi.org/10.XXX/XXXXX' + "published_doi", + "DOIs should be in the following format: https://doi.org/10.XXX/XXXXX", ) return doi_string @@ -445,10 +456,10 @@ def clean_published_doi(self): class RepositoryBase(forms.ModelForm): class Meta: model = models.Repository - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - self.press = kwargs.pop('press') + self.press = kwargs.pop("press") super(RepositoryBase, self).__init__(*args, **kwargs) @@ -456,18 +467,18 @@ class RepositoryInitial(RepositoryBase): class Meta: model = models.Repository fields = ( - 'name', - 'short_name', - 'domain', - 'object_name', - 'object_name_plural', - 'theme', - 'display_public_metrics', - 'publisher', + "name", + "short_name", + "domain", + "object_name", + "object_name_plural", + "theme", + "display_public_metrics", + "publisher", ) help_texts = { - 'domain': 'Using a custom domain requires configuring DNS. ' - 'The repository will always be available under the /code path', + "domain": "Using a custom domain requires configuring DNS. " + "The repository will always be available under the /code path", } def save(self, commit=True): @@ -481,29 +492,28 @@ def save(self, commit=True): class RepositorySite(RepositoryBase): - class Meta: model = models.Repository fields = ( - 'about', - 'logo', - 'hero_background', - 'favicon', - 'footer', - 'login_text', - 'limit_access_to_submission', - 'submission_access_request_text', - 'submission_access_contact', - 'review_submission_text', - 'custom_js_code', - 'review_helper', + "about", + "logo", + "hero_background", + "favicon", + "footer", + "login_text", + "limit_access_to_submission", + "submission_access_request_text", + "submission_access_contact", + "review_submission_text", + "custom_js_code", + "review_helper", ) widgets = { - 'about': TinyMCE, - 'footer': TinyMCE, - 'login_text': TinyMCE, - 'submission_access_request_text': TinyMCE, - 'review_helper': TinyMCE, + "about": TinyMCE, + "footer": TinyMCE, + "login_text": TinyMCE, + "submission_access_request_text": TinyMCE, + "review_helper": TinyMCE, } @@ -511,25 +521,25 @@ class RepositorySubmission(RepositoryBase): class Meta: model = models.Repository fields = ( - 'start', - 'file_upload_help', - 'submission_agreement', - 'limit_upload_to_pdf', - 'require_pdf_help', - 'additional_version_help', - 'managers', + "start", + "file_upload_help", + "submission_agreement", + "limit_upload_to_pdf", + "require_pdf_help", + "additional_version_help", + "managers", ) widgets = { - 'start': TinyMCE, - 'submission_agreement': TinyMCE, - 'file_upload_help': TinyMCE, - 'additional_version_help': TinyMCE, - 'managers': FilteredSelectMultiple( + "start": TinyMCE, + "submission_agreement": TinyMCE, + "file_upload_help": TinyMCE, + "additional_version_help": TinyMCE, + "managers": FilteredSelectMultiple( "Accounts", False, - attrs={'rows': '2'}, - ) + attrs={"rows": "2"}, + ), } @@ -537,61 +547,62 @@ class RepositoryEmails(RepositoryBase): class Meta: model = models.Repository fields = ( - 'submission', - 'publication', - 'decline', - 'accept_version', - 'decline_version', - 'new_comment', - 'review_invitation', - 'manager_review_status_change', - 'reviewer_review_status_change', - 'submission_notification_recipients', + "submission", + "publication", + "decline", + "accept_version", + "decline_version", + "new_comment", + "review_invitation", + "manager_review_status_change", + "reviewer_review_status_change", + "submission_notification_recipients", ) widgets = { - 'submission': TinyMCE, - 'publication': TinyMCE, - 'decline': TinyMCE, - 'accept_version': TinyMCE, - 'decline_version': TinyMCE, - 'new_comment': TinyMCE, - 'review_invitation': TinyMCE, - 'manager_review_status_change': TinyMCE, - 'reviewer_review_status_change': TinyMCE, - 'submission_notification_recipients': TableMultiSelectUser() + "submission": TinyMCE, + "publication": TinyMCE, + "decline": TinyMCE, + "accept_version": TinyMCE, + "decline_version": TinyMCE, + "new_comment": TinyMCE, + "review_invitation": TinyMCE, + "manager_review_status_change": TinyMCE, + "reviewer_review_status_change": TinyMCE, + "submission_notification_recipients": TableMultiSelectUser(), } def __init__(self, *args, **kwargs): super(RepositoryEmails, self).__init__(*args, **kwargs) - repo_managers = kwargs['instance'].managers.all() - self.fields['submission_notification_recipients'].queryset = repo_managers - self.fields['submission_notification_recipients'].choices = [(m.id, {"name": m.full_name(), "email": m.email}) for m in repo_managers] + repo_managers = kwargs["instance"].managers.all() + self.fields["submission_notification_recipients"].queryset = repo_managers + self.fields["submission_notification_recipients"].choices = [ + (m.id, {"name": m.full_name(), "email": m.email}) for m in repo_managers + ] + class RepositoryLiveForm(RepositoryBase): class Meta: model = models.Repository - fields = ( - 'live', - ) + fields = ("live",) class RepositoryFieldForm(forms.ModelForm): class Meta: model = models.RepositoryField fields = ( - 'name', - 'input_type', - 'choices', - 'required', - 'order', - 'help_text', - 'display', - 'dc_metadata_type', + "name", + "input_type", + "choices", + "required", + "order", + "help_text", + "display", + "dc_metadata_type", ) def __init__(self, *args, **kwargs): - self.repository = kwargs.pop('repository') + self.repository = kwargs.pop("repository") super(RepositoryFieldForm, self).__init__(*args, **kwargs) def save(self, commit=True): @@ -607,51 +618,51 @@ def save(self, commit=True): class PreprinttoArticleForm(forms.Form): license = forms.ModelChoiceField(queryset=submission_models.Licence.objects.none()) section = forms.ModelChoiceField(queryset=submission_models.Section.objects.none()) - stage = forms.ChoiceField( - choices=() - ) + stage = forms.ChoiceField(choices=()) force = forms.BooleanField( required=False, - help_text='If you want to force the creation of a new article object even if one exists, check this box. ' - 'The old article will be orphaned and no longer linked to this object.', + help_text="If you want to force the creation of a new article object even if one exists, check this box. " + "The old article will be orphaned and no longer linked to this object.", ) def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(PreprinttoArticleForm, self).__init__(*args, **kwargs) if self.journal: - self.fields['license'].queryset = submission_models.Licence.objects.filter( + self.fields["license"].queryset = submission_models.Licence.objects.filter( journal=self.journal, ) - self.fields['section'].queryset = submission_models.Section.objects.filter( + self.fields["section"].queryset = submission_models.Section.objects.filter( journal=self.journal, ) - self.fields['stage'].choices = workflow.workflow_journal_choices(self.journal) + self.fields["stage"].choices = workflow.workflow_journal_choices( + self.journal + ) class ReviewForm(forms.ModelForm): class Meta: model = models.Review fields = ( - 'reviewer', - 'date_due', + "reviewer", + "date_due", ) widgets = { - 'date_due': utils_forms.HTMLDateInput(), + "date_due": utils_forms.HTMLDateInput(), } def __init__(self, *args, **kwargs): - self.preprint = kwargs.pop('preprint') - self.manager = kwargs.pop('manager') + self.preprint = kwargs.pop("preprint") + self.manager = kwargs.pop("manager") super(ReviewForm, self).__init__(*args, **kwargs) - self.fields['reviewer'].queryset = self.preprint.repository.reviewer_accounts() + self.fields["reviewer"].queryset = self.preprint.repository.reviewer_accounts() def save(self, commit=True): review = super(ReviewForm, self).save(commit=False) review.preprint = self.preprint review.manager = self.manager - review.status = 'new' + review.status = "new" if commit: review.save() @@ -662,9 +673,9 @@ def save(self, commit=True): class ReviewDueDateForm(forms.ModelForm): class Meta: model = models.Review - fields = ('date_due',) + fields = ("date_due",) widgets = { - 'date_due': utils_forms.HTMLDateInput(), + "date_due": utils_forms.HTMLDateInput(), } @@ -677,20 +688,22 @@ class ReviewCommentForm(forms.Form): queryset=models.ReviewRecommendation.objects.none(), ) anonymous = forms.BooleanField( - help_text='Check if you want your comments to be displayed anonymously.', + help_text="Check if you want your comments to be displayed anonymously.", label="Comment Anonymously", required=False, ) def __init__(self, *args, **kwargs): - self.review = kwargs.pop('review') + self.review = kwargs.pop("review") super(ReviewCommentForm, self).__init__(*args, **kwargs) if self.review.comment: - self.fields['body'].initial = self.review.comment.body - self.fields['anonymous'].initial = self.review.anonymous - self.fields['recommendation'].initial = self.review.recommendation.pk + self.fields["body"].initial = self.review.comment.body + self.fields["anonymous"].initial = self.review.anonymous + self.fields["recommendation"].initial = self.review.recommendation.pk - self.fields['recommendation'].queryset = models.ReviewRecommendation.objects.filter( + self.fields[ + "recommendation" + ].queryset = models.ReviewRecommendation.objects.filter( repository=self.review.preprint.repository, active=True, ) @@ -704,12 +717,12 @@ def save(self): comment.author = self.review.reviewer comment.preprint = self.review.preprint - comment.body = self.cleaned_data.get('body') + comment.body = self.cleaned_data.get("body") comment.save() - self.review.anonymous = self.cleaned_data.get('anonymous', False) + self.review.anonymous = self.cleaned_data.get("anonymous", False) self.review.comment = comment - self.review.recommendation = self.cleaned_data.get('recommendation') + self.review.recommendation = self.cleaned_data.get("recommendation") self.review.save() @@ -717,12 +730,12 @@ class RecommendationForm(forms.ModelForm): class Meta: model = models.ReviewRecommendation fields = ( - 'name', - 'active', + "name", + "active", ) def __init__(self, *args, **kwargs): - self.repository = kwargs.pop('repository') + self.repository = kwargs.pop("repository") super(RecommendationForm, self).__init__(*args, **kwargs) def save(self, commit=True): diff --git a/src/repository/install.py b/src/repository/install.py index 701cf622b9..9bef46cb80 100644 --- a/src/repository/install.py +++ b/src/repository/install.py @@ -11,12 +11,12 @@ def load_settings(instance, force=True): """ path = os.path.join( settings.BASE_DIR, - 'utils/install/repository_settings.json', + "utils/install/repository_settings.json", ) with codecs.open( - os.path.join(path), - 'r+', - encoding='utf-8', + os.path.join(path), + "r+", + encoding="utf-8", ) as json_data: repo_settings = json.load(json_data) diff --git a/src/repository/logic.py b/src/repository/logic.py index 8753d12974..cd0d4b22d4 100755 --- a/src/repository/logic.py +++ b/src/repository/logic.py @@ -36,9 +36,9 @@ def get_month_day_range(date): def handle_file_upload(request, preprint): - if 'file' in request.FILES: - label = request.POST.get('label') - for uploaded_file in request.FILES.getlist('file'): + if "file" in request.FILES: + label = request.POST.get("label") + for uploaded_file in request.FILES.getlist("file"): save_galley( preprint, request, @@ -50,17 +50,17 @@ def handle_file_upload(request, preprint): def determine_action(preprint): if preprint.date_accepted and not preprint.date_declined: - return 'accept' + return "accept" else: - return 'decline' + return "decline" def get_pdf(article): try: try: - pdf = article.galley_set.get(file__mime_type='application/pdf') + pdf = article.galley_set.get(file__mime_type="application/pdf") except core_models.Galley.MultipleObjectsReturned: - pdf = article.galley_set.filter(file__mime_type='application/pdf')[0] + pdf = article.galley_set.filter(file__mime_type="application/pdf")[0] except core_models.Galley.DoesNotExist: pdf = None @@ -69,7 +69,7 @@ def get_pdf(article): def get_html(article): try: - galley = article.galley_set.get(type='html') + galley = article.galley_set.get(type="html") html = galley.file_content() except core_models.Galley.DoesNotExist: html = None @@ -79,9 +79,9 @@ def get_html(article): def get_publication_text(request, preprint, action): context = { - 'preprint': preprint, - 'request': request, - 'action': action, + "preprint": preprint, + "request": request, + "action": action, } if preprint.date_declined and not preprint.date_published: @@ -99,9 +99,9 @@ def get_publication_text(request, preprint, action): def raise_comment_event(request, comment): kwargs = { - 'request': request, - 'preprint': comment.preprint, - 'comment': comment, + "request": request, + "preprint": comment.preprint, + "comment": comment, } event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_COMMENT, @@ -111,17 +111,17 @@ def raise_comment_event(request, comment): messages.add_message( request, messages.SUCCESS, - 'Your comment has been saved. It has been sent for moderation.', + "Your comment has been saved. It has been sent for moderation.", ) def comment_manager_post(request, preprint): - if 'comment_public' in request.POST: - comment_id = request.POST.get('comment_public') - elif 'comment_reviewed' in request.POST: - comment_id = request.POST.get('comment_reviewed') - elif 'comment_delete' in request.POST: - comment_id = request.POST.get('comment_delete') + if "comment_public" in request.POST: + comment_id = request.POST.get("comment_public") + elif "comment_reviewed" in request.POST: + comment_id = request.POST.get("comment_reviewed") + elif "comment_delete" in request.POST: + comment_id = request.POST.get("comment_delete") else: return @@ -132,43 +132,52 @@ def comment_manager_post(request, preprint): preprint__repository=request.repository, ) - if 'comment_public' in request.POST: + if "comment_public" in request.POST: comment.toggle_public() - elif 'comment_reviewed' in request.POST: + elif "comment_reviewed" in request.POST: comment.mark_reviewed() - if 'comment_delete' in request.POST: + if "comment_delete" in request.POST: if request.user in request.repository.managers.all(): comment.delete() messages.add_message( request, messages.SUCCESS, - 'Comment deleted', + "Comment deleted", ) else: messages.add_message( request, messages.WARNING, - 'You do not have permission to delete this comment.', + "You do not have permission to delete this comment.", ) # TODO: Update this implementation def handle_author_post(request, preprint): - file = request.FILES.get('file') - update_type = request.POST.get('upload_type') - galley_id = request.POST.get('galley_id') + file = request.FILES.get("file") + update_type = request.POST.get("upload_type") + galley_id = request.POST.get("galley_id") galley = get_object_or_404(core_models.Galley, article=preprint, pk=galley_id) - if request.press.preprint_pdf_only and not files.check_in_memory_mime(in_memory_file=file) == 'application/pdf': - messages.add_message(request, messages.WARNING, 'You must upload a PDF file.') + if ( + request.press.preprint_pdf_only + and not files.check_in_memory_mime(in_memory_file=file) == "application/pdf" + ): + messages.add_message(request, messages.WARNING, "You must upload a PDF file.") return else: - file = files.save_file_to_article(file, preprint, request.user, label=galley.label) + file = files.save_file_to_article( + file, preprint, request.user, label=galley.label + ) - models.VersionQueue.objects.create(article=preprint, galley=galley, file=file, update_type=update_type) + models.VersionQueue.objects.create( + article=preprint, galley=galley, file=file, update_type=update_type + ) - messages.add_message(request, messages.INFO, 'This update has been added to the moderation queue.') + messages.add_message( + request, messages.INFO, "This update has been added to the moderation queue." + ) # TODO: Update this implementation @@ -180,10 +189,10 @@ def get_pending_update_from_post(request): """ update_id = None - if 'approve' in request.POST: - update_id = request.POST.get('approve') - elif 'decline' in request.POST: - update_id = request.POST.get('decline') + if "approve" in request.POST: + update_id = request.POST.get("approve") + elif "decline" in request.POST: + update_id = request.POST.get("decline") if update_id: pending_update = get_object_or_404( @@ -211,12 +220,12 @@ def approve_pending_update(request): messages.add_message( request, messages.INFO, - 'New version created.', + "New version created.", ) kwargs = { - 'pending_update': pending_update, - 'request': request, - 'action': 'accept', + "pending_update": pending_update, + "request": request, + "action": "accept", } event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_VERSION_UPDATE, @@ -226,10 +235,10 @@ def approve_pending_update(request): messages.add_message( request, messages.WARNING, - 'No valid pending update found.', + "No valid pending update found.", ) - return redirect(reverse('version_queue')) + return redirect(reverse("version_queue")) def decline_pending_update(request): @@ -240,13 +249,13 @@ def decline_pending_update(request): messages.add_message( request, messages.INFO, - 'New version declined.', + "New version declined.", ) kwargs = { - 'pending_update': pending_update, - 'request': request, - 'action': 'decline', - 'reason': request.POST.get('reason', 'No reason supplied.') + "pending_update": pending_update, + "request": request, + "action": "decline", + "reason": request.POST.get("reason", "No reason supplied."), } event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_VERSION_UPDATE, @@ -256,19 +265,16 @@ def decline_pending_update(request): messages.add_message( request, messages.WARNING, - 'No valid pending update found.', + "No valid pending update found.", ) - return redirect(reverse('version_queue')) + return redirect(reverse("version_queue")) def handle_delete_version(request, preprint): - version_id = request.POST.get('delete_version') + version_id = request.POST.get("delete_version") if not version_id: - messages.add_message( - request, - messages.WARNING, - 'No version id supplied') + messages.add_message(request, messages.WARNING, "No version id supplied") else: version = get_object_or_404( models.PreprintVersion, @@ -279,7 +285,7 @@ def handle_delete_version(request, preprint): messages.add_message( request, messages.INFO, - 'Version deleted.', + "Version deleted.", ) @@ -291,25 +297,17 @@ def delete_file(request, preprint): :param preprint: Preprint object :return: None """ - file_id = request.POST.get('delete_file') + file_id = request.POST.get("delete_file") if not file_id: - messages.add_message( - request, - messages.WARNING, - 'No File ID supplied.' - ) + messages.add_message(request, messages.WARNING, "No File ID supplied.") try: models.PreprintFile.objects.get( pk=file_id, preprint=preprint, ).delete() except models.PreprintFile.DoesNotExist: - messages.add_message( - request, - messages.WARNING, - 'No matching File found.' - ) + messages.add_message(request, messages.WARNING, "No matching File found.") def subject_article_pks(user): @@ -322,23 +320,25 @@ def subject_article_pks(user): def get_unpublished_preprints(request, user_subject_pks): - author_name_subq = models.PreprintAuthor.objects.filter( - preprint=OuterRef('pk') - ).annotate( - full_name=Concat( - "account__first_name", Value(" "), "account__last_name", - output_field=CharField(), + author_name_subq = ( + models.PreprintAuthor.objects.filter(preprint=OuterRef("pk")) + .annotate( + full_name=Concat( + "account__first_name", + Value(" "), + "account__last_name", + output_field=CharField(), + ) ) - ).values('full_name') + .values("full_name") + ) unpublished_preprints = models.Preprint.objects.filter( date_published__isnull=True, date_submitted__isnull=False, date_declined__isnull=True, date_accepted__isnull=True, repository=request.repository, - ).annotate( - author_full_name=Subquery(author_name_subq[:1]) - ) + ).annotate(author_full_name=Subquery(author_name_subq[:1])) if request.user.is_staff or request.user.is_repository_manager(request.repository): return unpublished_preprints @@ -347,21 +347,23 @@ def get_unpublished_preprints(request, user_subject_pks): def get_published_preprints(request, user_subject_pks): - author_name_subq = models.PreprintAuthor.objects.filter( - preprint=OuterRef('pk') - ).annotate( - full_name=Concat( - "account__first_name", Value(" "), "account__last_name", - output_field=CharField(), + author_name_subq = ( + models.PreprintAuthor.objects.filter(preprint=OuterRef("pk")) + .annotate( + full_name=Concat( + "account__first_name", + Value(" "), + "account__last_name", + output_field=CharField(), + ) ) - ).values('full_name') + .values("full_name") + ) published_preprints = models.Preprint.objects.filter( date_published__isnull=False, date_submitted__isnull=False, repository=request.repository, - ).annotate( - author_full_name=Subquery(author_name_subq[:1]) - ) + ).annotate(author_full_name=Subquery(author_name_subq[:1])) if request.user.is_staff or request.user.is_repository_manager(request.repository): return published_preprints @@ -397,8 +399,8 @@ def get_doi(request, preprint): else: doi = render_template.get_message_content( request, - {'preprint': preprint}, - request.press.get_setting_value('Crossref Pattern'), + {"preprint": preprint}, + request.press.get_setting_value("Crossref Pattern"), template_is_setting=True, ) return doi @@ -410,13 +412,14 @@ def get_list_of_preprint_journals(): :return: Queryset of Journal objects """ from journal import models as journal_models + journals = journal_models.Journal.objects.all() journals_accepting_preprints = list() for journal in journals: setting = journal.get_setting( - 'general', - 'accepts_preprint_submissions', + "general", + "accepts_preprint_submissions", ) if setting: journals_accepting_preprints.append(journal) @@ -426,11 +429,11 @@ def get_list_of_preprint_journals(): def check_duplicates(version_queue): preprints = [version_request.preprint for version_request in version_queue] - return [k for k,v in Counter(preprints).items() if v > 1] + return [k for k, v in Counter(preprints).items() if v > 1] def search_for_authors(request, preprint): - search_term = request.POST.get('search') + search_term = request.POST.get("search") try: search_author = core_models.Account.objects.get( Q(email=search_term) | Q(orcid=search_term) @@ -438,34 +441,30 @@ def search_for_authors(request, preprint): pa, created = models.PreprintAuthor.objects.get_or_create( account=search_author, preprint=preprint, - defaults={'order': preprint.next_author_order()}, + defaults={"order": preprint.next_author_order()}, ) if not created: messages.add_message( request, messages.WARNING, - 'This author is already associated with this {}'.format( + "This author is already associated with this {}".format( request.repository.object_name, - ) + ), ) return pa except core_models.Account.DoesNotExist: - messages.add_message( - request, - messages.INFO, - 'No author found.' - ) + messages.add_message(request, messages.INFO, "No author found.") def raise_event(event_type, request, preprint): kwargs = { - 'request': request, - 'preprint': preprint, + "request": request, + "preprint": preprint, } - if event_type == 'accept': + if event_type == "accept": event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_PUBLICATION, **kwargs, @@ -474,16 +473,15 @@ def raise_event(event_type, request, preprint): def store_preprint_access(request, preprint, file=None): try: - user_agent = parse_ua_string(request.META.get('HTTP_USER_AGENT', None)) + user_agent = parse_ua_string(request.META.get("HTTP_USER_AGENT", None)) except TypeError: user_agent = None if user_agent and not user_agent.is_bot: - ip = shared.get_ip_address(request) iso_country_code = get_iso_country_code(ip) country = iso_to_country_object(iso_country_code) - counter_tracking_id = request.session.get('counter_tracking') + counter_tracking_id = request.session.get("counter_tracking") identifier = counter_tracking_id if counter_tracking_id else hash(ip) # Check if someone with this identifier has accessed the same file @@ -496,7 +494,6 @@ def store_preprint_access(request, preprint, file=None): identifier=identifier, accessed__gte=time_to_check, ).exists(): - models.PreprintAccess.objects.create( preprint=preprint, file=file, @@ -508,18 +505,18 @@ def store_preprint_access(request, preprint, file=None): def get_review_notification(request, preprint, review): url = request.repository.site_url( path=reverse( - 'repository_submit_review', + "repository_submit_review", kwargs={ - 'review_id': review.pk, - 'access_code': review.access_code, - } + "review_id": review.pk, + "access_code": review.access_code, + }, ) ) context = { - 'preprint': preprint, - 'request': request, - 'review': review, - 'url': url, + "preprint": preprint, + "request": request, + "review": review, + "url": url, } template = request.repository.review_invitation email_content = render_template.get_message_content( diff --git a/src/repository/management/commands/generate_preprints.py b/src/repository/management/commands/generate_preprints.py index 32ace81096..25fc7b0565 100644 --- a/src/repository/management/commands/generate_preprints.py +++ b/src/repository/management/commands/generate_preprints.py @@ -16,16 +16,16 @@ class Command(BaseCommand): help = "Allows an admin to generate random preprints." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('short_name') - parser.add_argument('number', nargs='?', default=1, type=int) - parser.add_argument('owner', nargs='?', default=None) + parser.add_argument("short_name") + parser.add_argument("number", nargs="?", default=1, type=int) + parser.add_argument("owner", nargs="?", default=None) parser.add_argument( - '--metrics', + "--metrics", type=int, default=None, help="number of PreprintAccess to create by article", @@ -38,10 +38,10 @@ def handle(self, *args, **options): :param options: None :return: None """ - short_name = options.get('short_name') - number = options.get('number') - owner = options.get('owner') - metrics = options.get('metrics', 0) + short_name = options.get("short_name") + number = options.get("number") + owner = options.get("owner") + metrics = options.get("metrics", 0) fake = Faker() try: @@ -49,20 +49,20 @@ def handle(self, *args, **options): short_name=short_name, ) except models.Repository.DoesNotExist: - exit('No repository found.') + exit("No repository found.") if owner: try: owner = core_models.Account.objects.get(pk=owner) except core_models.Account.DoesNotExist: - exit('Owner not found.') + exit("Owner not found.") else: owner = core_models.Account.objects.all().first() subjects = [ - 'Computing', - 'Software Engineering', - 'Web Development', + "Computing", + "Software Engineering", + "Web Development", ] for subject in subjects: @@ -80,7 +80,7 @@ def handle(self, *args, **options): models.STAGE_PREPRINT_PUBLISHED, ] - for x in range(0,number): + for x in range(0, number): preprint = models.Preprint.objects.create( repository=repo, owner=owner, @@ -97,22 +97,21 @@ def handle(self, *args, **options): preprint_file = models.PreprintFile.objects.create( preprint=preprint, - original_filename='fake_file.pdf', - mime_type='application/pdf', + original_filename="fake_file.pdf", + mime_type="application/pdf", size=100, ) preprint.submission_file = preprint_file for y in range(0, 1): - fake_email = '{uuid}@example.com'.format(uuid=uuid4()) + fake_email = "{uuid}@example.com".format(uuid=uuid4()) account = core_models.Account.objects.create( first_name=fake.first_name(), last_name=fake.last_name(), email=fake_email, username=fake_email, institution=fake.sentence(), - ) models.PreprintAuthor.objects.create( preprint=preprint, @@ -136,12 +135,15 @@ def handle(self, *args, **options): if metrics: print("Inserting Metrics...") - models.PreprintAccess.objects.bulk_create([ - models.PreprintAccess( - preprint=preprint, - file=preprint_file, - ) for i in range(0, metrics)]) - + models.PreprintAccess.objects.bulk_create( + [ + models.PreprintAccess( + preprint=preprint, + file=preprint_file, + ) + for i in range(0, metrics) + ] + ) preprint.save() print(f"Created {preprint.title} with ID {preprint.pk}") diff --git a/src/repository/management/commands/update_repository_settings.py b/src/repository/management/commands/update_repository_settings.py index 480421d02a..c729e0c104 100644 --- a/src/repository/management/commands/update_repository_settings.py +++ b/src/repository/management/commands/update_repository_settings.py @@ -4,18 +4,18 @@ class Command(BaseCommand): - """ A management command to update repository settings.""" + """A management command to update repository settings.""" help = "Installs a journal." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--short_name', default=False) - parser.add_argument('--force', action='store_true', default=False) + parser.add_argument("--short_name", default=False) + parser.add_argument("--force", action="store_true", default=False) def handle(self, *args, **options): """Updates repository settings for a given repository. @@ -25,8 +25,8 @@ def handle(self, *args, **options): are not provided, they will be requested via raw_input. --force will overwrite all settings. :return: None """ - short_name = options.get('short_name', None) - force = options.get('force') + short_name = options.get("short_name", None) + force = options.get("force") if short_name: repos = models.Repository.objects.filter(short_name=short_name) @@ -34,7 +34,7 @@ def handle(self, *args, **options): repos = models.Repository.objects.all() if not repos: - print('No repositories found.') + print("No repositories found.") exit() for repo in repos: diff --git a/src/repository/migrations/0001_initial.py b/src/repository/migrations/0001_initial.py index 13027f2889..8a9110744c 100644 --- a/src/repository/migrations/0001_initial.py +++ b/src/repository/migrations/0001_initial.py @@ -11,247 +11,637 @@ class Migration(migrations.Migration): - initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('submission', '0043_auto_20200523_1158'), - ('press', '0021_auto_20190329_1202'), + ("submission", "0043_auto_20200523_1158"), + ("press", "0021_auto_20190329_1202"), ] operations = [ migrations.CreateModel( - name='Author', + name="Author", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('email_address', models.EmailField(max_length=254, unique=True)), - ('first_name', models.CharField(max_length=255)), - ('middle_name', models.CharField(blank=True, max_length=255, null=True)), - ('last_name', models.CharField(max_length=255)), - ('affiliation', models.TextField(blank=True, null=True)), - ('orcid', models.CharField(blank=True, max_length=255, null=True, verbose_name='ORCID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("email_address", models.EmailField(max_length=254, unique=True)), + ("first_name", models.CharField(max_length=255)), + ( + "middle_name", + models.CharField(blank=True, max_length=255, null=True), + ), + ("last_name", models.CharField(max_length=255)), + ("affiliation", models.TextField(blank=True, null=True)), + ( + "orcid", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="ORCID" + ), + ), ], options={ - 'ordering': ('last_name',), + "ordering": ("last_name",), }, ), migrations.CreateModel( - name='Comment', + name="Comment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('body', models.TextField(verbose_name='Write your comment:')), - ('is_reviewed', models.BooleanField(default=False)), - ('is_public', models.BooleanField(default=False)), - ('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ("body", models.TextField(verbose_name="Write your comment:")), + ("is_reviewed", models.BooleanField(default=False)), + ("is_public", models.BooleanField(default=False)), + ( + "author", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('-date_time', '-pk'), + "ordering": ("-date_time", "-pk"), }, ), migrations.CreateModel( - name='Preprint', + name="Preprint", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stage', models.CharField(default='preprint_unsubmitted', max_length=25)), - ('title', models.CharField(help_text='Your article title', max_length=300)), - ('abstract', models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True)), - ('meta_image', models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/home/ajrbyers/janeway/src/media'), upload_to=repository.models.preprint_file_upload)), - ('comments_editor', models.TextField(blank=True, help_text="Add any comments you'd like the editor to consider here.", null=True, verbose_name='Comments to the Editor')), - ('doi', models.CharField(blank=True, max_length=100, null=True)), - ('preprint_decision_notification', models.BooleanField(default=False)), - ('date_started', models.DateTimeField(default=django.utils.timezone.now)), - ('date_submitted', models.DateTimeField(blank=True, null=True)), - ('date_accepted', models.DateTimeField(blank=True, null=True)), - ('date_declined', models.DateTimeField(blank=True, null=True)), - ('date_published', models.DateTimeField(blank=True, null=True)), - ('date_updated', models.DateTimeField(blank=True, null=True)), - ('current_step', models.IntegerField(default=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "stage", + models.CharField(default="preprint_unsubmitted", max_length=25), + ), + ( + "title", + models.CharField(help_text="Your article title", max_length=300), + ), + ( + "abstract", + models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), + ), + ( + "meta_image", + models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/home/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), + ), + ( + "comments_editor", + models.TextField( + blank=True, + help_text="Add any comments you'd like the editor to consider here.", + null=True, + verbose_name="Comments to the Editor", + ), + ), + ("doi", models.CharField(blank=True, max_length=100, null=True)), + ("preprint_decision_notification", models.BooleanField(default=False)), + ( + "date_started", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("date_submitted", models.DateTimeField(blank=True, null=True)), + ("date_accepted", models.DateTimeField(blank=True, null=True)), + ("date_declined", models.DateTimeField(blank=True, null=True)), + ("date_published", models.DateTimeField(blank=True, null=True)), + ("date_updated", models.DateTimeField(blank=True, null=True)), + ("current_step", models.IntegerField(default=1)), ], ), migrations.CreateModel( - name='PreprintAccess', + name="PreprintAccess", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('accessed', models.DateTimeField(auto_now_add=True)), - ('location', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("accessed", models.DateTimeField(auto_now_add=True)), + ("location", models.CharField(max_length=10)), ], ), migrations.CreateModel( - name='PreprintAuthor', + name="PreprintAuthor", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=0)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Author')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=0)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Author", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.CreateModel( - name='PreprintFile', + name="PreprintFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.FileField(storage=core.file_system.JanewayFileSystemStorage(location='files/'), upload_to=repository.models.preprint_file_upload)), - ('original_filename', models.TextField()), - ('uploaded', models.DateTimeField(default=django.utils.timezone.now)), - ('mime_type', models.CharField(blank=True, max_length=255, null=True)), - ('size', models.PositiveIntegerField(default=0)), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "file", + models.FileField( + storage=core.file_system.JanewayFileSystemStorage( + location="files/" + ), + upload_to=repository.models.preprint_file_upload, + ), + ), + ("original_filename", models.TextField()), + ("uploaded", models.DateTimeField(default=django.utils.timezone.now)), + ("mime_type", models.CharField(blank=True, max_length=255, null=True)), + ("size", models.PositiveIntegerField(default=0)), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], ), migrations.CreateModel( - name='PreprintVersion', + name="PreprintVersion", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('version', models.IntegerField(default=1)), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("version", models.IntegerField(default=1)), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], options={ - 'ordering': ('-version', '-date_time', '-id'), + "ordering": ("-version", "-date_time", "-id"), }, ), migrations.CreateModel( - name='Repository', + name="Repository", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('domain', models.CharField(default='www.example.com', max_length=255, unique=True)), - ('is_secure', models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.')), - ('name', models.CharField(max_length=255)), - ('short_name', models.CharField(max_length=15)), - ('object_name', models.CharField(help_text='eg. preprint or article', max_length=255)), - ('object_name_plural', models.CharField(help_text='eg. preprints or articles', max_length=255)), - ('logo', models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/home/ajrbyers/janeway/src/media'), upload_to=repository.models.repo_media_upload)), - ('publisher', models.CharField(help_text='Used for outputs including DC and Citation metadata', max_length=255)), - ('custom_js_code', models.TextField(blank=True, help_text='The contents of this field are output into the JS areaat the foot of every Repository page.', null=True)), - ('live', models.BooleanField(default=False)), - ('limit_upload_to_pdf', models.BooleanField(default=False, help_text='If set to True, this will require all file uploads fromauthors to be PDF files.')), - ('about', models.TextField(blank=True, null=True)), - ('start', models.TextField(blank=True, null=True)), - ('submission', models.TextField(blank=True, null=True)), - ('publication', models.TextField(blank=True, null=True)), - ('decline', models.TextField(blank=True, null=True)), - ('random_homepage_preprints', models.BooleanField(default=False)), - ('homepage_preprints', models.ManyToManyField(blank=True, to='submission.Article')), - ('managers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), - ('press', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='press.Press')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "domain", + models.CharField( + default="www.example.com", max_length=255, unique=True + ), + ), + ( + "is_secure", + models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), + ), + ("name", models.CharField(max_length=255)), + ("short_name", models.CharField(max_length=15)), + ( + "object_name", + models.CharField( + help_text="eg. preprint or article", max_length=255 + ), + ), + ( + "object_name_plural", + models.CharField( + help_text="eg. preprints or articles", max_length=255 + ), + ), + ( + "logo", + models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/home/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), + ), + ( + "publisher", + models.CharField( + help_text="Used for outputs including DC and Citation metadata", + max_length=255, + ), + ), + ( + "custom_js_code", + models.TextField( + blank=True, + help_text="The contents of this field are output into the JS areaat the foot of every Repository page.", + null=True, + ), + ), + ("live", models.BooleanField(default=False)), + ( + "limit_upload_to_pdf", + models.BooleanField( + default=False, + help_text="If set to True, this will require all file uploads fromauthors to be PDF files.", + ), + ), + ("about", models.TextField(blank=True, null=True)), + ("start", models.TextField(blank=True, null=True)), + ("submission", models.TextField(blank=True, null=True)), + ("publication", models.TextField(blank=True, null=True)), + ("decline", models.TextField(blank=True, null=True)), + ("random_homepage_preprints", models.BooleanField(default=False)), + ( + "homepage_preprints", + models.ManyToManyField(blank=True, to="submission.Article"), + ), + ( + "managers", + models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), + ), + ( + "press", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="press.Press" + ), + ), ], options={ - 'verbose_name_plural': 'repositories', + "verbose_name_plural": "repositories", }, ), migrations.CreateModel( - name='RepositoryField', + name="RepositoryField", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('input_type', models.CharField(choices=[('text', 'Text'), ('select', 'Dropdown'), ('checkbox', 'Checkbox'), ('number', 'Number'), ('date', 'Date')], max_length=255)), - ('choices', models.CharField(blank=True, help_text='Separate choices with the bar | character.', max_length=1000, null=True)), - ('required', models.BooleanField(default=True)), - ('order', models.IntegerField()), - ('help_text', models.TextField(blank=True, null=True)), - ('display', models.BooleanField(default=False, help_text='Whether or not display this field in the article page')), - ('dc_metadata_type', models.CharField(help_text='If this field is to be output as a dc metadata field you can addthe type here.', max_length=255)), - ('repository', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Repository')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "input_type", + models.CharField( + choices=[ + ("text", "Text"), + ("select", "Dropdown"), + ("checkbox", "Checkbox"), + ("number", "Number"), + ("date", "Date"), + ], + max_length=255, + ), + ), + ( + "choices", + models.CharField( + blank=True, + help_text="Separate choices with the bar | character.", + max_length=1000, + null=True, + ), + ), + ("required", models.BooleanField(default=True)), + ("order", models.IntegerField()), + ("help_text", models.TextField(blank=True, null=True)), + ( + "display", + models.BooleanField( + default=False, + help_text="Whether or not display this field in the article page", + ), + ), + ( + "dc_metadata_type", + models.CharField( + help_text="If this field is to be output as a dc metadata field you can addthe type here.", + max_length=255, + ), + ), + ( + "repository", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Repository", + ), + ), ], ), migrations.CreateModel( - name='RepositoryFieldAnswer', + name="RepositoryFieldAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField()), - ('field', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.RepositoryField')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("answer", models.TextField()), + ( + "field", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.RepositoryField", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], ), migrations.CreateModel( - name='Subject', + name="Subject", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField(blank=True)), - ('enabled', models.BooleanField(default=True, help_text='If disabled, this subject will not appear publicly.')), - ('editors', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='repository.Subject')), - ('repository', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Repository')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(blank=True)), + ( + "enabled", + models.BooleanField( + default=True, + help_text="If disabled, this subject will not appear publicly.", + ), + ), + ( + "editors", + models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), + ), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="children", + to="repository.Subject", + ), + ), + ( + "repository", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Repository", + ), + ), ], options={ - 'ordering': ('slug', 'pk'), + "ordering": ("slug", "pk"), }, ), migrations.CreateModel( - name='VersionQueue', + name="VersionQueue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('update_type', models.CharField(choices=[('correction', 'Correction'), ('version', 'New Version')], max_length=10)), - ('date_submitted', models.DateTimeField(default=django.utils.timezone.now)), - ('date_decision', models.DateTimeField(blank=True, null=True)), - ('approved', models.BooleanField(default=False)), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "update_type", + models.CharField( + choices=[ + ("correction", "Correction"), + ("version", "New Version"), + ], + max_length=10, + ), + ), + ( + "date_submitted", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("date_decision", models.DateTimeField(blank=True, null=True)), + ("approved", models.BooleanField(default=False)), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], ), migrations.AddField( - model_name='preprintaccess', - name='file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile'), + model_name="preprintaccess", + name="file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), ), migrations.AddField( - model_name='preprintaccess', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint'), + model_name="preprintaccess", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Preprint" + ), ), migrations.AddField( - model_name='preprint', - name='curent_version', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='curent_version', to='repository.PreprintVersion'), + model_name="preprint", + name="curent_version", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="curent_version", + to="repository.PreprintVersion", + ), ), migrations.AddField( - model_name='preprint', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, to='submission.Keyword'), + model_name="preprint", + name="keywords", + field=models.ManyToManyField( + blank=True, null=True, to="submission.Keyword" + ), ), migrations.AddField( - model_name='preprint', - name='license', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Licence'), + model_name="preprint", + name="license", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Licence", + ), ), migrations.AddField( - model_name='preprint', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="preprint", + name="owner", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='preprint', - name='repository', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Repository'), + model_name="preprint", + name="repository", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Repository", + ), ), migrations.AddField( - model_name='preprint', - name='subject', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Subject'), + model_name="preprint", + name="subject", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Subject", + ), ), migrations.AddField( - model_name='preprint', - name='submission_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submission_file', to='repository.PreprintFile'), + model_name="preprint", + name="submission_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="submission_file", + to="repository.PreprintFile", + ), ), migrations.AddField( - model_name='comment', - name='preprint', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Preprint'), + model_name="comment", + name="preprint", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Preprint", + ), ), migrations.AddField( - model_name='comment', - name='reply_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Comment'), + model_name="comment", + name="reply_to", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Comment", + ), ), migrations.AlterUniqueTogether( - name='preprintauthor', - unique_together=set([('author', 'preprint')]), + name="preprintauthor", + unique_together=set([("author", "preprint")]), ), ] diff --git a/src/repository/migrations/0001_squashed_0019_auto_20201030_1423.py b/src/repository/migrations/0001_squashed_0019_auto_20201030_1423.py index 46ace9efc3..9bc6cd10c4 100644 --- a/src/repository/migrations/0001_squashed_0019_auto_20201030_1423.py +++ b/src/repository/migrations/0001_squashed_0019_auto_20201030_1423.py @@ -11,422 +11,981 @@ class Migration(migrations.Migration): - - replaces = [('repository', '0001_initial'), ('repository', '0002_preprintversion_moderated_version'), ('repository', '0003_auto_20200617_1541'), ('repository', '0004_auto_20200718_0933'), ('repository', '0005_repository_new_comment'), ('repository', '0006_auto_20200810_1426'), ('repository', '0007_auto_20200813_1156'), ('repository', '0008_auto_20200814_1433'), ('repository', '0009_auto_20200820_1212'), ('repository', '0010_auto_20200901_0737'), ('repository', '0011_auto_20200924_1941'), ('repository', '0012_auto_20200925_1933'), ('repository', '0013_auto_20200925_1946'), ('repository', '0014_auto_20200925_2013'), ('repository', '0015_repository_submission_agreement'), ('repository', '0016_auto_20200930_1617'), ('repository', '0017_auto_20200930_1629'), ('repository', '0018_auto_20201027_1110'), ('repository', '0019_auto_20201030_1423')] + replaces = [ + ("repository", "0001_initial"), + ("repository", "0002_preprintversion_moderated_version"), + ("repository", "0003_auto_20200617_1541"), + ("repository", "0004_auto_20200718_0933"), + ("repository", "0005_repository_new_comment"), + ("repository", "0006_auto_20200810_1426"), + ("repository", "0007_auto_20200813_1156"), + ("repository", "0008_auto_20200814_1433"), + ("repository", "0009_auto_20200820_1212"), + ("repository", "0010_auto_20200901_0737"), + ("repository", "0011_auto_20200924_1941"), + ("repository", "0012_auto_20200925_1933"), + ("repository", "0013_auto_20200925_1946"), + ("repository", "0014_auto_20200925_2013"), + ("repository", "0015_repository_submission_agreement"), + ("repository", "0016_auto_20200930_1617"), + ("repository", "0017_auto_20200930_1629"), + ("repository", "0018_auto_20201027_1110"), + ("repository", "0019_auto_20201030_1423"), + ] initial = True dependencies = [ - ('core', '0040_auto_20200529_1415'), - ('submission', '0043_auto_20200523_1158'), + ("core", "0040_auto_20200529_1415"), + ("submission", "0043_auto_20200523_1158"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('press', '0021_auto_20190329_1202'), + ("press", "0021_auto_20190329_1202"), ] operations = [ migrations.CreateModel( - name='Author', + name="Author", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('email_address', models.EmailField(max_length=254, unique=True)), - ('first_name', models.CharField(max_length=255)), - ('middle_name', models.CharField(blank=True, max_length=255, null=True)), - ('last_name', models.CharField(max_length=255)), - ('affiliation', models.TextField(blank=True, null=True)), - ('orcid', models.CharField(blank=True, max_length=255, null=True, verbose_name='ORCID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("email_address", models.EmailField(max_length=254, unique=True)), + ("first_name", models.CharField(max_length=255)), + ( + "middle_name", + models.CharField(blank=True, max_length=255, null=True), + ), + ("last_name", models.CharField(max_length=255)), + ("affiliation", models.TextField(blank=True, null=True)), + ( + "orcid", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="ORCID" + ), + ), ], options={ - 'ordering': ('last_name',), + "ordering": ("last_name",), }, ), migrations.CreateModel( - name='Comment', + name="Comment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('body', models.TextField(verbose_name='Write your comment:')), - ('is_reviewed', models.BooleanField(default=False)), - ('is_public', models.BooleanField(default=False)), - ('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ("body", models.TextField(verbose_name="Write your comment:")), + ("is_reviewed", models.BooleanField(default=False)), + ("is_public", models.BooleanField(default=False)), + ( + "author", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('-date_time', '-pk'), + "ordering": ("-date_time", "-pk"), }, ), migrations.CreateModel( - name='Preprint', + name="Preprint", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stage', models.CharField(default='preprint_unsubmitted', max_length=25)), - ('title', models.CharField(help_text='Your article title', max_length=300)), - ('abstract', models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True)), - ('meta_image', models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/home/ajrbyers/janeway/src/media'), upload_to=repository.models.preprint_file_upload)), - ('comments_editor', models.TextField(blank=True, help_text="Add any comments you'd like the editor to consider here.", null=True, verbose_name='Comments to the Editor')), - ('doi', models.CharField(blank=True, max_length=100, null=True)), - ('preprint_decision_notification', models.BooleanField(default=False)), - ('date_started', models.DateTimeField(default=django.utils.timezone.now)), - ('date_submitted', models.DateTimeField(blank=True, null=True)), - ('date_accepted', models.DateTimeField(blank=True, null=True)), - ('date_declined', models.DateTimeField(blank=True, null=True)), - ('date_published', models.DateTimeField(blank=True, null=True)), - ('date_updated', models.DateTimeField(blank=True, null=True)), - ('current_step', models.IntegerField(default=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "stage", + models.CharField(default="preprint_unsubmitted", max_length=25), + ), + ( + "title", + models.CharField(help_text="Your article title", max_length=300), + ), + ( + "abstract", + models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), + ), + ( + "meta_image", + models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/home/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), + ), + ( + "comments_editor", + models.TextField( + blank=True, + help_text="Add any comments you'd like the editor to consider here.", + null=True, + verbose_name="Comments to the Editor", + ), + ), + ("doi", models.CharField(blank=True, max_length=100, null=True)), + ("preprint_decision_notification", models.BooleanField(default=False)), + ( + "date_started", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("date_submitted", models.DateTimeField(blank=True, null=True)), + ("date_accepted", models.DateTimeField(blank=True, null=True)), + ("date_declined", models.DateTimeField(blank=True, null=True)), + ("date_published", models.DateTimeField(blank=True, null=True)), + ("date_updated", models.DateTimeField(blank=True, null=True)), + ("current_step", models.IntegerField(default=1)), ], ), migrations.CreateModel( - name='PreprintAccess', + name="PreprintAccess", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('accessed', models.DateTimeField(auto_now_add=True)), - ('location', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("accessed", models.DateTimeField(auto_now_add=True)), + ("location", models.CharField(max_length=10)), ], ), migrations.CreateModel( - name='PreprintAuthor', + name="PreprintAuthor", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=0)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Author')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=0)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Author", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.CreateModel( - name='PreprintFile', + name="PreprintFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.FileField(storage=core.file_system.JanewayFileSystemStorage(location='files/'), upload_to=repository.models.preprint_file_upload)), - ('original_filename', models.TextField()), - ('uploaded', models.DateTimeField(default=django.utils.timezone.now)), - ('mime_type', models.CharField(blank=True, max_length=255, null=True)), - ('size', models.PositiveIntegerField(default=0)), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "file", + models.FileField( + storage=core.file_system.JanewayFileSystemStorage( + location="files/" + ), + upload_to=repository.models.preprint_file_upload, + ), + ), + ("original_filename", models.TextField()), + ("uploaded", models.DateTimeField(default=django.utils.timezone.now)), + ("mime_type", models.CharField(blank=True, max_length=255, null=True)), + ("size", models.PositiveIntegerField(default=0)), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], ), migrations.CreateModel( - name='PreprintVersion', + name="PreprintVersion", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('version', models.IntegerField(default=1)), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("version", models.IntegerField(default=1)), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ( + "file", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], options={ - 'ordering': ('-version', '-date_time', '-id'), + "ordering": ("-version", "-date_time", "-id"), }, ), migrations.CreateModel( - name='Repository', + name="Repository", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('domain', models.CharField(default='www.example.com', max_length=255, unique=True)), - ('is_secure', models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.')), - ('name', models.CharField(max_length=255)), - ('short_name', models.CharField(max_length=15)), - ('object_name', models.CharField(help_text='eg. preprint or article', max_length=255)), - ('object_name_plural', models.CharField(help_text='eg. preprints or articles', max_length=255)), - ('logo', models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/home/ajrbyers/janeway/src/media'), upload_to=repository.models.repo_media_upload)), - ('publisher', models.CharField(help_text='Used for outputs including DC and Citation metadata', max_length=255)), - ('custom_js_code', models.TextField(blank=True, help_text='The contents of this field are output into the JS areaat the foot of every Repository page.', null=True)), - ('live', models.BooleanField(default=False)), - ('limit_upload_to_pdf', models.BooleanField(default=False, help_text='If set to True, this will require all file uploads fromauthors to be PDF files.')), - ('about', models.TextField(blank=True, null=True)), - ('start', models.TextField(blank=True, null=True)), - ('submission', models.TextField(blank=True, null=True)), - ('publication', models.TextField(blank=True, null=True)), - ('decline', models.TextField(blank=True, null=True)), - ('random_homepage_preprints', models.BooleanField(default=False)), - ('homepage_preprints', models.ManyToManyField(blank=True, to='submission.Article')), - ('managers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), - ('press', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='press.Press')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "domain", + models.CharField( + default="www.example.com", max_length=255, unique=True + ), + ), + ( + "is_secure", + models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), + ), + ("name", models.CharField(max_length=255)), + ("short_name", models.CharField(max_length=15)), + ( + "object_name", + models.CharField( + help_text="eg. preprint or article", max_length=255 + ), + ), + ( + "object_name_plural", + models.CharField( + help_text="eg. preprints or articles", max_length=255 + ), + ), + ( + "logo", + models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/home/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), + ), + ( + "publisher", + models.CharField( + help_text="Used for outputs including DC and Citation metadata", + max_length=255, + ), + ), + ( + "custom_js_code", + models.TextField( + blank=True, + help_text="The contents of this field are output into the JS areaat the foot of every Repository page.", + null=True, + ), + ), + ("live", models.BooleanField(default=False)), + ( + "limit_upload_to_pdf", + models.BooleanField( + default=False, + help_text="If set to True, this will require all file uploads fromauthors to be PDF files.", + ), + ), + ("about", models.TextField(blank=True, null=True)), + ("start", models.TextField(blank=True, null=True)), + ("submission", models.TextField(blank=True, null=True)), + ("publication", models.TextField(blank=True, null=True)), + ("decline", models.TextField(blank=True, null=True)), + ("random_homepage_preprints", models.BooleanField(default=False)), + ( + "homepage_preprints", + models.ManyToManyField(blank=True, to="submission.Article"), + ), + ( + "managers", + models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), + ), + ( + "press", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="press.Press" + ), + ), ], options={ - 'verbose_name_plural': 'repositories', + "verbose_name_plural": "repositories", }, ), migrations.CreateModel( - name='RepositoryField', + name="RepositoryField", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('input_type', models.CharField(choices=[('text', 'Text'), ('select', 'Dropdown'), ('checkbox', 'Checkbox'), ('number', 'Number'), ('date', 'Date')], max_length=255)), - ('choices', models.CharField(blank=True, help_text='Separate choices with the bar | character.', max_length=1000, null=True)), - ('required', models.BooleanField(default=True)), - ('order', models.IntegerField()), - ('help_text', models.TextField(blank=True, null=True)), - ('display', models.BooleanField(default=False, help_text='Whether or not display this field in the article page')), - ('dc_metadata_type', models.CharField(help_text='If this field is to be output as a dc metadata field you can addthe type here.', max_length=255)), - ('repository', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Repository')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "input_type", + models.CharField( + choices=[ + ("text", "Text"), + ("select", "Dropdown"), + ("checkbox", "Checkbox"), + ("number", "Number"), + ("date", "Date"), + ], + max_length=255, + ), + ), + ( + "choices", + models.CharField( + blank=True, + help_text="Separate choices with the bar | character.", + max_length=1000, + null=True, + ), + ), + ("required", models.BooleanField(default=True)), + ("order", models.IntegerField()), + ("help_text", models.TextField(blank=True, null=True)), + ( + "display", + models.BooleanField( + default=False, + help_text="Whether or not display this field in the article page", + ), + ), + ( + "dc_metadata_type", + models.CharField( + help_text="If this field is to be output as a dc metadata field you can addthe type here.", + max_length=255, + ), + ), + ( + "repository", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Repository", + ), + ), ], ), migrations.CreateModel( - name='RepositoryFieldAnswer', + name="RepositoryFieldAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField()), - ('field', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.RepositoryField')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("answer", models.TextField()), + ( + "field", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.RepositoryField", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), ], ), migrations.CreateModel( - name='Subject', + name="Subject", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField(blank=True, max_length=255)), - ('enabled', models.BooleanField(default=True, help_text='If disabled, this subject will not appear publicly.')), - ('editors', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='repository.Subject')), - ('repository', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Repository')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(blank=True, max_length=255)), + ( + "enabled", + models.BooleanField( + default=True, + help_text="If disabled, this subject will not appear publicly.", + ), + ), + ( + "editors", + models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), + ), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="children", + to="repository.Subject", + ), + ), + ( + "repository", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Repository", + ), + ), ], options={ - 'ordering': ('slug', 'pk'), + "ordering": ("slug", "pk"), }, ), migrations.CreateModel( - name='VersionQueue', + name="VersionQueue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('update_type', models.CharField(choices=[('correction', 'Text Correction'), ('metadata_correction', 'Metadata Correction'), ('version', 'New Version')], max_length=20)), - ('date_submitted', models.DateTimeField(default=django.utils.timezone.now)), - ('date_decision', models.DateTimeField(blank=True, null=True)), - ('approved', models.BooleanField(default=False)), - ('file', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile')), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), - ('abstract', models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True)), - ('title', models.CharField(default='', help_text='Your article title', max_length=300)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "update_type", + models.CharField( + choices=[ + ("correction", "Text Correction"), + ("metadata_correction", "Metadata Correction"), + ("version", "New Version"), + ], + max_length=20, + ), + ), + ( + "date_submitted", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("date_decision", models.DateTimeField(blank=True, null=True)), + ("approved", models.BooleanField(default=False)), + ( + "file", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), + ( + "abstract", + models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), + ), + ( + "title", + models.CharField( + default="", help_text="Your article title", max_length=300 + ), + ), ], ), migrations.AddField( - model_name='preprintaccess', - name='file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile'), + model_name="preprintaccess", + name="file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), ), migrations.AddField( - model_name='preprintaccess', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint'), + model_name="preprintaccess", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Preprint" + ), ), migrations.AddField( - model_name='preprint', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, to='submission.Keyword'), + model_name="preprint", + name="keywords", + field=models.ManyToManyField( + blank=True, null=True, to="submission.Keyword" + ), ), migrations.AddField( - model_name='preprint', - name='license', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Licence'), + model_name="preprint", + name="license", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Licence", + ), ), migrations.AddField( - model_name='preprint', - name='owner', - field=models.ForeignKey(help_text='The account that submitted this item.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="preprint", + name="owner", + field=models.ForeignKey( + help_text="The account that submitted this item.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='preprint', - name='repository', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Repository'), + model_name="preprint", + name="repository", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Repository", + ), ), migrations.AddField( - model_name='preprint', - name='submission_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submission_file', to='repository.PreprintFile'), + model_name="preprint", + name="submission_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="submission_file", + to="repository.PreprintFile", + ), ), migrations.AddField( - model_name='comment', - name='preprint', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Preprint'), + model_name="comment", + name="preprint", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Preprint", + ), ), migrations.AddField( - model_name='comment', - name='reply_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.Comment'), + model_name="comment", + name="reply_to", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.Comment", + ), ), migrations.AlterUniqueTogether( - name='preprintauthor', - unique_together=set([('author', 'preprint')]), + name="preprintauthor", + unique_together=set([("author", "preprint")]), ), migrations.AddField( - model_name='preprintversion', - name='moderated_version', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.VersionQueue'), + model_name="preprintversion", + name="moderated_version", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.VersionQueue", + ), ), migrations.AddField( - model_name='repository', - name='accept_version', + model_name="repository", + name="accept_version", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='repository', - name='decline_version', + model_name="repository", + name="decline_version", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='live', - field=models.BooleanField(default=False, verbose_name='Repository is Live?'), + model_name="repository", + name="live", + field=models.BooleanField( + default=False, verbose_name="Repository is Live?" + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repository', - name='short_name', - field=models.CharField(help_text='Shortened version of the name eg. olh. Max 15 chars.', max_length=15), + model_name="repository", + name="short_name", + field=models.CharField( + help_text="Shortened version of the name eg. olh. Max 15 chars.", + max_length=15, + ), ), migrations.AlterField( - model_name='repository', - name='start', - field=models.TextField(blank=True, null=True, verbose_name='Submission Start Text'), + model_name="repository", + name="start", + field=models.TextField( + blank=True, null=True, verbose_name="Submission Start Text" + ), ), migrations.AddField( - model_name='repository', - name='new_comment', + model_name="repository", + name="new_comment", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='preprint', - name='subject', - field=models.ManyToManyField(null=True, to='repository.Subject'), + model_name="preprint", + name="subject", + field=models.ManyToManyField(null=True, to="repository.Subject"), ), migrations.RemoveField( - model_name='preprintaccess', - name='location', + model_name="preprintaccess", + name="location", ), migrations.AddField( - model_name='preprintaccess', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Country'), + model_name="preprintaccess", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Country", + ), ), migrations.AddField( - model_name='preprintaccess', - name='identifier', + model_name="preprintaccess", + name="identifier", field=models.TextField(blank=True, null=True), ), migrations.AlterModelOptions( - name='repositoryfield', - options={'ordering': ('order', 'name')}, + name="repositoryfield", + options={"ordering": ("order", "name")}, ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/ajrbyers/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/ajrbyers/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repositoryfield', - name='dc_metadata_type', - field=models.CharField(blank=True, help_text='If this field is to be output as a dc metadata field you can addthe type here.', max_length=255, null=True), + model_name="repositoryfield", + name="dc_metadata_type", + field=models.CharField( + blank=True, + help_text="If this field is to be output as a dc metadata field you can addthe type here.", + max_length=255, + null=True, + ), ), migrations.CreateModel( - name='PreprintSupplementaryFile', + name="PreprintSupplementaryFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField()), - ('label', models.CharField(default='Supplementary File', max_length=200, verbose_name='Label')), - ('order', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("url", models.URLField()), + ( + "label", + models.CharField( + default="Supplementary File", + max_length=200, + verbose_name="Label", + ), + ), + ("order", models.PositiveIntegerField(default=0)), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.AddField( - model_name='preprint', - name='preprint_doi', - field=models.CharField(blank=True, help_text='System supplied DOI. ', max_length=100, null=True, verbose_name='Preprint DOI'), + model_name="preprint", + name="preprint_doi", + field=models.CharField( + blank=True, + help_text="System supplied DOI. ", + max_length=100, + null=True, + verbose_name="Preprint DOI", + ), ), migrations.AlterField( - model_name='preprint', - name='doi', - field=models.CharField(blank=True, help_text="You can add a DOI linking to this item's published version using this field. Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.", max_length=100, null=True, verbose_name='Published DOI'), + model_name="preprint", + name="doi", + field=models.CharField( + blank=True, + help_text="You can add a DOI linking to this item's published version using this field. Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.", + max_length=100, + null=True, + verbose_name="Published DOI", + ), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/btingle/code/j2/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/btingle/code/j2/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/btingle/code/j2/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/btingle/code/j2/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='preprintsupplementaryfile', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint'), + model_name="preprintsupplementaryfile", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Preprint" + ), ), migrations.AlterUniqueTogether( - name='preprintsupplementaryfile', - unique_together=set([('url', 'preprint')]), + name="preprintsupplementaryfile", + unique_together=set([("url", "preprint")]), ), migrations.AddField( - model_name='repository', - name='footer', - field=models.TextField(blank=True, default='

Powered by Janeway

', null=True), + model_name="repository", + name="footer", + field=models.TextField( + blank=True, default="

Powered by Janeway

", null=True + ), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='repository', - name='login_text', - field=models.TextField(blank=True, help_text='If text is added it will display on the login and register pages.', null=True, verbose_name='Account Page Text'), + model_name="repository", + name="login_text", + field=models.TextField( + blank=True, + help_text="If text is added it will display on the login and register pages.", + null=True, + verbose_name="Account Page Text", + ), ), migrations.AddField( - model_name='repository', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='repository', - name='hero_background', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="hero_background", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='repository', - name='submission_agreement', - field=models.TextField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True), + model_name="repository", + name="submission_agreement", + field=models.TextField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='preprintversion', - name='abstract', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="preprintversion", + name="abstract", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='preprintversion', - name='title', - field=models.CharField(default='', help_text='Your article title', max_length=300), + model_name="preprintversion", + name="title", + field=models.CharField( + default="", help_text="Your article title", max_length=300 + ), preserve_default=False, ), ] diff --git a/src/repository/migrations/0002_preprintversion_moderated_version.py b/src/repository/migrations/0002_preprintversion_moderated_version.py index 3ad4baa190..1662571a80 100644 --- a/src/repository/migrations/0002_preprintversion_moderated_version.py +++ b/src/repository/migrations/0002_preprintversion_moderated_version.py @@ -7,15 +7,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0001_initial'), + ("repository", "0001_initial"), ] operations = [ migrations.AddField( - model_name='preprintversion', - name='moderated_version', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.VersionQueue'), + model_name="preprintversion", + name="moderated_version", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.VersionQueue", + ), ), ] diff --git a/src/repository/migrations/0003_auto_20200617_1541.py b/src/repository/migrations/0003_auto_20200617_1541.py index 8b3262c085..b0ac452c32 100644 --- a/src/repository/migrations/0003_auto_20200617_1541.py +++ b/src/repository/migrations/0003_auto_20200617_1541.py @@ -6,20 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0002_preprintversion_moderated_version'), + ("repository", "0002_preprintversion_moderated_version"), ] operations = [ migrations.AddField( - model_name='repository', - name='accept_version', + model_name="repository", + name="accept_version", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='repository', - name='decline_version', + model_name="repository", + name="decline_version", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/repository/migrations/0004_auto_20200718_0933.py b/src/repository/migrations/0004_auto_20200718_0933.py index 166ff3b354..93da30759c 100644 --- a/src/repository/migrations/0004_auto_20200718_0933.py +++ b/src/repository/migrations/0004_auto_20200718_0933.py @@ -8,40 +8,60 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0003_auto_20200617_1541'), + ("repository", "0003_auto_20200617_1541"), ] operations = [ migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='live', - field=models.BooleanField(default=False, verbose_name='Repository is Live?'), + model_name="repository", + name="live", + field=models.BooleanField( + default=False, verbose_name="Repository is Live?" + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repository', - name='short_name', - field=models.CharField(help_text='Shortened version of the name eg. olh. Max 15 chars.', max_length=15), + model_name="repository", + name="short_name", + field=models.CharField( + help_text="Shortened version of the name eg. olh. Max 15 chars.", + max_length=15, + ), ), migrations.AlterField( - model_name='repository', - name='start', - field=models.TextField(blank=True, null=True, verbose_name='Submission Start Text'), + model_name="repository", + name="start", + field=models.TextField( + blank=True, null=True, verbose_name="Submission Start Text" + ), ), migrations.AlterField( - model_name='subject', - name='slug', + model_name="subject", + name="slug", field=models.SlugField(blank=True, max_length=255), ), ] diff --git a/src/repository/migrations/0005_repository_new_comment.py b/src/repository/migrations/0005_repository_new_comment.py index c46f4b514e..ff6d5ed792 100644 --- a/src/repository/migrations/0005_repository_new_comment.py +++ b/src/repository/migrations/0005_repository_new_comment.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0004_auto_20200718_0933'), + ("repository", "0004_auto_20200718_0933"), ] operations = [ migrations.AddField( - model_name='repository', - name='new_comment', + model_name="repository", + name="new_comment", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/repository/migrations/0006_auto_20200810_1426.py b/src/repository/migrations/0006_auto_20200810_1426.py index 41b10ba139..1314495349 100644 --- a/src/repository/migrations/0006_auto_20200810_1426.py +++ b/src/repository/migrations/0006_auto_20200810_1426.py @@ -7,19 +7,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0005_repository_new_comment'), + ("repository", "0005_repository_new_comment"), ] operations = [ migrations.RemoveField( - model_name='preprint', - name='curent_version', + model_name="preprint", + name="curent_version", ), migrations.AlterField( - model_name='subject', - name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='repository.Subject'), + model_name="subject", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="children", + to="repository.Subject", + ), ), ] diff --git a/src/repository/migrations/0007_auto_20200813_1156.py b/src/repository/migrations/0007_auto_20200813_1156.py index 2e02a922d4..8c7fd7eeda 100644 --- a/src/repository/migrations/0007_auto_20200813_1156.py +++ b/src/repository/migrations/0007_auto_20200813_1156.py @@ -6,19 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0006_auto_20200810_1426'), + ("repository", "0006_auto_20200810_1426"), ] operations = [ migrations.RemoveField( - model_name='preprint', - name='subject', + model_name="preprint", + name="subject", ), migrations.AddField( - model_name='preprint', - name='subject', - field=models.ManyToManyField(to='repository.Subject'), + model_name="preprint", + name="subject", + field=models.ManyToManyField(to="repository.Subject"), ), ] diff --git a/src/repository/migrations/0008_auto_20200814_1433.py b/src/repository/migrations/0008_auto_20200814_1433.py index cba7754d2b..bcd0dcdc15 100644 --- a/src/repository/migrations/0008_auto_20200814_1433.py +++ b/src/repository/migrations/0008_auto_20200814_1433.py @@ -7,30 +7,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0040_auto_20200529_1415'), - ('repository', '0007_auto_20200813_1156'), + ("core", "0040_auto_20200529_1415"), + ("repository", "0007_auto_20200813_1156"), ] operations = [ migrations.RemoveField( - model_name='preprintaccess', - name='location', + model_name="preprintaccess", + name="location", ), migrations.AddField( - model_name='preprintaccess', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Country'), + model_name="preprintaccess", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Country", + ), ), migrations.AddField( - model_name='preprintaccess', - name='identifier', + model_name="preprintaccess", + name="identifier", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='preprint', - name='subject', - field=models.ManyToManyField(null=True, to='repository.Subject'), + model_name="preprint", + name="subject", + field=models.ManyToManyField(null=True, to="repository.Subject"), ), ] diff --git a/src/repository/migrations/0009_auto_20200820_1212.py b/src/repository/migrations/0009_auto_20200820_1212.py index 37683baa5a..1b249b78b9 100644 --- a/src/repository/migrations/0009_auto_20200820_1212.py +++ b/src/repository/migrations/0009_auto_20200820_1212.py @@ -8,29 +8,47 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0008_auto_20200814_1433'), + ("repository", "0008_auto_20200814_1433"), ] operations = [ migrations.AlterModelOptions( - name='repositoryfield', - options={'ordering': ('order', 'name')}, + name="repositoryfield", + options={"ordering": ("order", "name")}, ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/ajrbyers/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/ajrbyers/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/ajrbyers/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repositoryfield', - name='dc_metadata_type', - field=models.CharField(blank=True, help_text='If this field is to be output as a dc metadata field you can addthe type here.', max_length=255, null=True), + model_name="repositoryfield", + name="dc_metadata_type", + field=models.CharField( + blank=True, + help_text="If this field is to be output as a dc metadata field you can addthe type here.", + max_length=255, + null=True, + ), ), ] diff --git a/src/repository/migrations/0010_auto_20200901_0737.py b/src/repository/migrations/0010_auto_20200901_0737.py index 01b85572fa..2e36f0b228 100644 --- a/src/repository/migrations/0010_auto_20200901_0737.py +++ b/src/repository/migrations/0010_auto_20200901_0737.py @@ -10,56 +10,103 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0009_auto_20200820_1212'), + ("repository", "0009_auto_20200820_1212"), ] operations = [ migrations.CreateModel( - name='PreprintSupplementaryFile', + name="PreprintSupplementaryFile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField()), - ('label', models.CharField(default='Supplementary File', max_length=200, verbose_name='Label')), - ('order', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("url", models.URLField()), + ( + "label", + models.CharField( + default="Supplementary File", + max_length=200, + verbose_name="Label", + ), + ), + ("order", models.PositiveIntegerField(default=0)), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.AddField( - model_name='preprint', - name='preprint_doi', - field=models.CharField(blank=True, help_text='System supplied DOI. ', max_length=100, null=True, verbose_name='Preprint DOI'), + model_name="preprint", + name="preprint_doi", + field=models.CharField( + blank=True, + help_text="System supplied DOI. ", + max_length=100, + null=True, + verbose_name="Preprint DOI", + ), ), migrations.AlterField( - model_name='preprint', - name='doi', - field=models.CharField(blank=True, help_text="You can add a DOI linking to this item's published version using this field. Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.", max_length=100, null=True, verbose_name='Published DOI'), + model_name="preprint", + name="doi", + field=models.CharField( + blank=True, + help_text="You can add a DOI linking to this item's published version using this field. Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.", + max_length=100, + null=True, + verbose_name="Published DOI", + ), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/btingle/code/j2/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/btingle/code/j2/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='preprint', - name='owner', - field=models.ForeignKey(help_text='The account that submitted this item.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="preprint", + name="owner", + field=models.ForeignKey( + help_text="The account that submitted this item.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/Users/btingle/code/j2/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/Users/btingle/code/j2/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='preprintsupplementaryfile', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint'), + model_name="preprintsupplementaryfile", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Preprint" + ), ), migrations.AlterUniqueTogether( - name='preprintsupplementaryfile', - unique_together=set([('url', 'preprint')]), + name="preprintsupplementaryfile", + unique_together=set([("url", "preprint")]), ), ] diff --git a/src/repository/migrations/0011_auto_20200924_1941.py b/src/repository/migrations/0011_auto_20200924_1941.py index cf5510bbf1..0f99ffe058 100644 --- a/src/repository/migrations/0011_auto_20200924_1941.py +++ b/src/repository/migrations/0011_auto_20200924_1941.py @@ -8,25 +8,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0010_auto_20200901_0737'), + ("repository", "0010_auto_20200901_0737"), ] operations = [ migrations.AddField( - model_name='repository', - name='footer', - field=models.TextField(blank=True, default='Powered by Janeway', null=True), + model_name="repository", + name="footer", + field=models.TextField(blank=True, default="Powered by Janeway", null=True), ), migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), ] diff --git a/src/repository/migrations/0012_auto_20200925_1933.py b/src/repository/migrations/0012_auto_20200925_1933.py index 04d8e6f920..d2e96fda75 100644 --- a/src/repository/migrations/0012_auto_20200925_1933.py +++ b/src/repository/migrations/0012_auto_20200925_1933.py @@ -6,20 +6,25 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0011_auto_20200924_1941'), + ("repository", "0011_auto_20200924_1941"), ] operations = [ migrations.AddField( - model_name='repository', - name='login_text', - field=models.TextField(blank=True, help_text='If text is added it will display on the login page.', null=True), + model_name="repository", + name="login_text", + field=models.TextField( + blank=True, + help_text="If text is added it will display on the login page.", + null=True, + ), ), migrations.AlterField( - model_name='repository', - name='footer', - field=models.TextField(blank=True, default='

Powered by Janeway

', null=True), + model_name="repository", + name="footer", + field=models.TextField( + blank=True, default="

Powered by Janeway

", null=True + ), ), ] diff --git a/src/repository/migrations/0013_auto_20200925_1946.py b/src/repository/migrations/0013_auto_20200925_1946.py index 89e2925df3..e7a991cf18 100644 --- a/src/repository/migrations/0013_auto_20200925_1946.py +++ b/src/repository/migrations/0013_auto_20200925_1946.py @@ -8,20 +8,33 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0012_auto_20200925_1933'), + ("repository", "0012_auto_20200925_1933"), ] operations = [ migrations.AddField( - model_name='repository', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AddField( - model_name='repository', - name='hero_background', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(location='/vol/janeway/src/media'), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="hero_background", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage( + location="/vol/janeway/src/media" + ), + upload_to=repository.models.repo_media_upload, + ), ), ] diff --git a/src/repository/migrations/0014_auto_20200925_2013.py b/src/repository/migrations/0014_auto_20200925_2013.py index e711582457..e5ccbaefc3 100644 --- a/src/repository/migrations/0014_auto_20200925_2013.py +++ b/src/repository/migrations/0014_auto_20200925_2013.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0013_auto_20200925_1946'), + ("repository", "0013_auto_20200925_1946"), ] operations = [ migrations.AlterField( - model_name='repository', - name='login_text', - field=models.TextField(blank=True, help_text='If text is added it will display on the login and register pages.', null=True, verbose_name='Account Page Text'), + model_name="repository", + name="login_text", + field=models.TextField( + blank=True, + help_text="If text is added it will display on the login and register pages.", + null=True, + verbose_name="Account Page Text", + ), ), ] diff --git a/src/repository/migrations/0015_repository_submission_agreement.py b/src/repository/migrations/0015_repository_submission_agreement.py index 4519f2e3e9..e75236b9c8 100644 --- a/src/repository/migrations/0015_repository_submission_agreement.py +++ b/src/repository/migrations/0015_repository_submission_agreement.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0014_auto_20200925_2013'), + ("repository", "0014_auto_20200925_2013"), ] operations = [ migrations.AddField( - model_name='repository', - name='submission_agreement', - field=models.TextField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True), + model_name="repository", + name="submission_agreement", + field=models.TextField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), ), ] diff --git a/src/repository/migrations/0016_auto_20200930_1617.py b/src/repository/migrations/0016_auto_20200930_1617.py index e90dd8b908..ba234b32f5 100644 --- a/src/repository/migrations/0016_auto_20200930_1617.py +++ b/src/repository/migrations/0016_auto_20200930_1617.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0015_repository_submission_agreement'), + ("repository", "0015_repository_submission_agreement"), ] operations = [ migrations.AlterField( - model_name='repository', - name='submission_agreement', - field=models.TextField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True), + model_name="repository", + name="submission_agreement", + field=models.TextField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), ), ] diff --git a/src/repository/migrations/0017_auto_20200930_1629.py b/src/repository/migrations/0017_auto_20200930_1629.py index 06835b7e0c..898ddf952a 100644 --- a/src/repository/migrations/0017_auto_20200930_1629.py +++ b/src/repository/migrations/0017_auto_20200930_1629.py @@ -9,30 +9,49 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0016_auto_20200930_1617'), + ("repository", "0016_auto_20200930_1617"), ] operations = [ migrations.AlterField( - model_name='preprint', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.preprint_file_upload), + model_name="preprint", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.preprint_file_upload, + ), ), migrations.AlterField( - model_name='repository', - name='favicon', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="favicon", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repository', - name='hero_background', - field=core.model_utils.SVGImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="hero_background", + field=core.model_utils.SVGImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=core.model_utils.SVGImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=core.model_utils.SVGImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), ] diff --git a/src/repository/migrations/0018_auto_20201027_1110.py b/src/repository/migrations/0018_auto_20201027_1110.py index 7aecab46db..654a479d95 100644 --- a/src/repository/migrations/0018_auto_20201027_1110.py +++ b/src/repository/migrations/0018_auto_20201027_1110.py @@ -6,32 +6,43 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0017_auto_20200930_1629'), + ("repository", "0017_auto_20200930_1629"), ] operations = [ migrations.AddField( - model_name='preprintversion', - name='abstract', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="preprintversion", + name="abstract", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='preprintversion', - name='title', - field=models.CharField(default='', help_text='Your article title', max_length=300), + model_name="preprintversion", + name="title", + field=models.CharField( + default="", help_text="Your article title", max_length=300 + ), preserve_default=False, ), migrations.AddField( - model_name='versionqueue', - name='abstract', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="versionqueue", + name="abstract", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='versionqueue', - name='title', - field=models.CharField(default='', help_text='Your article title', max_length=300), + model_name="versionqueue", + name="title", + field=models.CharField( + default="", help_text="Your article title", max_length=300 + ), preserve_default=False, ), ] diff --git a/src/repository/migrations/0018_auto_20201109_1024.py b/src/repository/migrations/0018_auto_20201109_1024.py index 0ab949c14c..bb3bb5474b 100644 --- a/src/repository/migrations/0018_auto_20201109_1024.py +++ b/src/repository/migrations/0018_auto_20201109_1024.py @@ -6,15 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0017_auto_20200930_1629'), + ("repository", "0017_auto_20200930_1629"), ] operations = [ migrations.AlterField( - model_name='repositoryfield', - name='input_type', - field=models.CharField(choices=[('text', 'Text'), ('select', 'Dropdown'), ('checkbox', 'Checkbox'), ('number', 'Number'), ('date', 'Date'), ('textarea', 'Text Area')], max_length=255), + model_name="repositoryfield", + name="input_type", + field=models.CharField( + choices=[ + ("text", "Text"), + ("select", "Dropdown"), + ("checkbox", "Checkbox"), + ("number", "Number"), + ("date", "Date"), + ("textarea", "Text Area"), + ], + max_length=255, + ), ), ] diff --git a/src/repository/migrations/0019_auto_20201030_1423.py b/src/repository/migrations/0019_auto_20201030_1423.py index f83024bd77..7efec8b207 100644 --- a/src/repository/migrations/0019_auto_20201030_1423.py +++ b/src/repository/migrations/0019_auto_20201030_1423.py @@ -7,20 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0018_auto_20201027_1110'), + ("repository", "0018_auto_20201027_1110"), ] operations = [ migrations.AlterField( - model_name='versionqueue', - name='file', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.PreprintFile'), + model_name="versionqueue", + name="file", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.PreprintFile", + ), ), migrations.AlterField( - model_name='versionqueue', - name='update_type', - field=models.CharField(choices=[('correction', 'Text Correction'), ('metadata_correction', 'Metadata Correction'), ('version', 'New Version')], max_length=20), + model_name="versionqueue", + name="update_type", + field=models.CharField( + choices=[ + ("correction", "Text Correction"), + ("metadata_correction", "Metadata Correction"), + ("version", "New Version"), + ], + max_length=20, + ), ), ] diff --git a/src/repository/migrations/0020_vq_title_abstracts.py b/src/repository/migrations/0020_vq_title_abstracts.py index ebf8ff7242..89e0a424f9 100644 --- a/src/repository/migrations/0020_vq_title_abstracts.py +++ b/src/repository/migrations/0020_vq_title_abstracts.py @@ -6,7 +6,7 @@ def update_version_queues(apps, schema_editor): - VersionQueue = apps.get_model('repository', 'VersionQueue') + VersionQueue = apps.get_model("repository", "VersionQueue") for queue in VersionQueue.objects.all(): queue.title = queue.preprint.title @@ -15,9 +15,8 @@ def update_version_queues(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('repository', '0019_auto_20201030_1423'), + ("repository", "0019_auto_20201030_1423"), ] operations = [ @@ -25,4 +24,4 @@ class Migration(migrations.Migration): update_version_queues, reverse_code=migrations.RunPython.noop, ) - ] \ No newline at end of file + ] diff --git a/src/repository/migrations/0021_merge_20201116_1559.py b/src/repository/migrations/0021_merge_20201116_1559.py index eb9063021e..ba0c4eb8e8 100644 --- a/src/repository/migrations/0021_merge_20201116_1559.py +++ b/src/repository/migrations/0021_merge_20201116_1559.py @@ -8,21 +8,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0018_auto_20201109_1024'), - ('repository', '0020_vq_title_abstracts'), + ("repository", "0018_auto_20201109_1024"), + ("repository", "0020_vq_title_abstracts"), ] operations = [ migrations.AlterModelOptions( - name='author', + name="author", options={}, ), migrations.AlterField( - model_name='preprintauthor', - name='author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to='repository.Author'), + model_name="preprintauthor", + name="author", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.Author", + ), ), ] diff --git a/src/repository/migrations/0022_auto_20210303_1450.py b/src/repository/migrations/0022_auto_20210303_1450.py index bbf314449f..d5b10844cb 100644 --- a/src/repository/migrations/0022_auto_20210303_1450.py +++ b/src/repository/migrations/0022_auto_20210303_1450.py @@ -8,44 +8,47 @@ def normalise_preprint_author_orcids(apps, schema_editor): - Authors = apps.get_model('repository', 'Author') + Authors = apps.get_model("repository", "Author") for author in Authors.objects.filter(orcid__isnull=False): - split = author.orcid.split('/') + split = author.orcid.split("/") orcid = split[-1] author.orcid = orcid author.save() def create_accounts_for_authors(apps, schema_editor): - Account = apps.get_model('core', 'Account') - PreprintAuthor = apps.get_model('repository', 'PreprintAuthor') + Account = apps.get_model("core", "Account") + PreprintAuthor = apps.get_model("repository", "PreprintAuthor") for preprint_author in PreprintAuthor.objects.all(): acc, c = Account.objects.get_or_create( username=preprint_author.author.email_address.lower(), email=preprint_author.author.email_address.lower(), defaults={ - 'first_name': preprint_author.author.first_name, - 'middle_name': preprint_author.author.middle_name, - 'last_name': preprint_author.author.last_name, - 'institution': preprint_author.affiliation if preprint_author.affiliation else 'n/a', - 'orcid': preprint_author.author.orcid, - 'is_active': True, - } + "first_name": preprint_author.author.first_name, + "middle_name": preprint_author.author.middle_name, + "last_name": preprint_author.author.last_name, + "institution": preprint_author.affiliation + if preprint_author.affiliation + else "n/a", + "orcid": preprint_author.author.orcid, + "is_active": True, + }, ) preprint_author.account = acc preprint_author.save() if c: - print('New account created for Preprint author {}'.format(preprint_author)) + print("New account created for Preprint author {}".format(preprint_author)) else: - print('Account found and linked to Preprint author {}'.format(preprint_author)) + print( + "Account found and linked to Preprint author {}".format(preprint_author) + ) class Migration(migrations.Migration): - dependencies = [ - ('repository', '0021_merge_20201116_1559'), + ("repository", "0021_merge_20201116_1559"), ] operations = [ @@ -54,14 +57,17 @@ class Migration(migrations.Migration): reverse_code=migrations.RunPython.noop, ), migrations.AddField( - model_name='preprintauthor', - name='account', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL), + model_name="preprintauthor", + name="account", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='preprintauthor', - name='affiliation', + model_name="preprintauthor", + name="affiliation", field=models.TextField(blank=True, null=True), ), migrations.RunPython( diff --git a/src/repository/migrations/0023_auto_20210811_1317.py b/src/repository/migrations/0023_auto_20210811_1317.py index 5c0f3863da..4ad738abb0 100644 --- a/src/repository/migrations/0023_auto_20210811_1317.py +++ b/src/repository/migrations/0023_auto_20210811_1317.py @@ -8,15 +8,14 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('repository', '0022_auto_20210303_1450'), + ("repository", "0022_auto_20210303_1450"), ] operations = [ migrations.AlterUniqueTogether( - name='preprintauthor', - unique_together=set([('account', 'preprint')]), + name="preprintauthor", + unique_together=set([("account", "preprint")]), ), ] diff --git a/src/repository/migrations/0024_auto_20210812_1304.py b/src/repository/migrations/0024_auto_20210812_1304.py index a8a497291a..b03fce3f2f 100644 --- a/src/repository/migrations/0024_auto_20210812_1304.py +++ b/src/repository/migrations/0024_auto_20210812_1304.py @@ -6,20 +6,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0023_auto_20210811_1317'), + ("repository", "0023_auto_20210811_1317"), ] operations = [ migrations.AddField( - model_name='preprintversion', - name='published_doi', - field=models.URLField(blank=True, help_text='Please use the following format for your DOI: https://doi.org/10.xxxx/xxxx', max_length=255, null=True, verbose_name='Published Article DOI'), + model_name="preprintversion", + name="published_doi", + field=models.URLField( + blank=True, + help_text="Please use the following format for your DOI: https://doi.org/10.xxxx/xxxx", + max_length=255, + null=True, + verbose_name="Published Article DOI", + ), ), migrations.AddField( - model_name='versionqueue', - name='published_doi', - field=models.URLField(blank=True, help_text='Please use the following format for your DOI: https://doi.org/10.xxxx/xxxx', max_length=255, null=True, verbose_name='Published Article DOI'), + model_name="versionqueue", + name="published_doi", + field=models.URLField( + blank=True, + help_text="Please use the following format for your DOI: https://doi.org/10.xxxx/xxxx", + max_length=255, + null=True, + verbose_name="Published Article DOI", + ), ), ] diff --git a/src/repository/migrations/0025_create_keyword_preprint.py b/src/repository/migrations/0025_create_keyword_preprint.py index 3da253778d..778780b3e5 100644 --- a/src/repository/migrations/0025_create_keyword_preprint.py +++ b/src/repository/migrations/0025_create_keyword_preprint.py @@ -8,8 +8,8 @@ def migrate_keywords(apps, schema_editor): - Preprint = apps.get_model('repository', 'Preprint') - KeywordPreprint = apps.get_model('repository', 'KeywordPreprint') + Preprint = apps.get_model("repository", "Preprint") + KeywordPreprint = apps.get_model("repository", "KeywordPreprint") for preprint in Preprint.objects.all(): for i, kw in enumerate(preprint.keywords.all()): @@ -17,58 +17,75 @@ def migrate_keywords(apps, schema_editor): keyword=kw, preprint=preprint, defaults={ - 'order': i, - } + "order": i, + }, ) def demigrate_keywords(apps, schema_editor): - KeywordPreprint = apps.get_model('submission', 'KeywordArticle') + KeywordPreprint = apps.get_model("submission", "KeywordArticle") for kw_preprint in KeywordPreprint.objects.all(): kw_preprint.article.keywords.add(kw_preprint.keyword) class Migration(migrations.Migration): - dependencies = [ - ('submission', '0058_auto_20210812_1254'), - ('repository', '0024_auto_20210812_1304'), + ("submission", "0058_auto_20210812_1254"), + ("repository", "0024_auto_20210812_1304"), ] operations = [ migrations.CreateModel( - name='KeywordPreprint', + name="KeywordPreprint", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=1)), - ('keyword', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Keyword')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=1)), + ( + "keyword", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Keyword", + ), + ), ], options={ - 'ordering': ['order'], + "ordering": ["order"], }, ), migrations.AddField( - model_name='keywordpreprint', - name='preprint', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint'), + model_name="keywordpreprint", + name="preprint", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Preprint" + ), ), - # Migrate keywords onto KeywordPreprint before we change Preprint.keywords migrations.RunPython(migrate_keywords, reverse_code=demigrate_keywords), - migrations.RemoveField( - model_name='preprint', - name='keywords', + model_name="preprint", + name="keywords", ), migrations.AddField( - model_name='preprint', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, through='repository.KeywordPreprint', - to='submission.Keyword'), + model_name="preprint", + name="keywords", + field=models.ManyToManyField( + blank=True, + null=True, + through="repository.KeywordPreprint", + to="submission.Keyword", + ), ), migrations.AlterUniqueTogether( - name='keywordpreprint', - unique_together=set([('keyword', 'preprint')]), + name="keywordpreprint", + unique_together=set([("keyword", "preprint")]), ), ] diff --git a/src/repository/migrations/0025_preprint_preprint_decline_note.py b/src/repository/migrations/0025_preprint_preprint_decline_note.py index 43ae5cb795..da10525cb1 100644 --- a/src/repository/migrations/0025_preprint_preprint_decline_note.py +++ b/src/repository/migrations/0025_preprint_preprint_decline_note.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0024_auto_20210812_1304'), + ("repository", "0024_auto_20210812_1304"), ] operations = [ migrations.AddField( - model_name='preprint', - name='preprint_decline_note', + model_name="preprint", + name="preprint_decline_note", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/repository/migrations/0026_merge_20211011_0906.py b/src/repository/migrations/0026_merge_20211011_0906.py index 5ec0867c31..8e61b8eecd 100644 --- a/src/repository/migrations/0026_merge_20211011_0906.py +++ b/src/repository/migrations/0026_merge_20211011_0906.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0025_preprint_preprint_decline_note'), - ('repository', '0025_create_keyword_preprint'), + ("repository", "0025_preprint_preprint_decline_note"), + ("repository", "0025_create_keyword_preprint"), ] - operations = [ - ] + operations = [] diff --git a/src/repository/migrations/0027_auto_20220107_1213.py b/src/repository/migrations/0027_auto_20220107_1213.py index 54c6c7f0f1..02d3b280d1 100644 --- a/src/repository/migrations/0027_auto_20220107_1213.py +++ b/src/repository/migrations/0027_auto_20220107_1213.py @@ -10,44 +10,58 @@ def remove_stale_domains(apps, schema_editor): - if hasattr(settings, 'URL_CONFIG') and settings.URL_CONFIG == 'path': - Repository = apps.get_model('repository', 'Repository') + if hasattr(settings, "URL_CONFIG") and settings.URL_CONFIG == "path": + Repository = apps.get_model("repository", "Repository") Repository.objects.all().update(domain=None) class Migration(migrations.Migration): - dependencies = [ - ('repository', '0026_merge_20211011_0906'), + ("repository", "0026_merge_20211011_0906"), ] operations = [ migrations.AlterField( - model_name='repository', - name='domain', + model_name="repository", + name="domain", field=models.CharField(blank=True, max_length=255, null=True, unique=True), ), migrations.RunPython( remove_stale_domains, reverse_code=migrations.RunPython.noop ), migrations.AlterField( - model_name='preprint', - name='keywords', - field=core.model_utils.M2MOrderedThroughField(blank=True, null=True, through='repository.KeywordPreprint', to='submission.Keyword'), + model_name="preprint", + name="keywords", + field=core.model_utils.M2MOrderedThroughField( + blank=True, + null=True, + through="repository.KeywordPreprint", + to="submission.Keyword", + ), ), migrations.AlterField( - model_name='repository', - name='hero_background', - field=core.model_utils.SVGImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="hero_background", + field=core.model_utils.SVGImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='repository', - name='logo', - field=core.model_utils.SVGImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=repository.models.repo_media_upload), + model_name="repository", + name="logo", + field=core.model_utils.SVGImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=repository.models.repo_media_upload, + ), ), migrations.AlterField( - model_name='versionqueue', - name='title', - field=models.CharField(help_text='Your article title', max_length=300), + model_name="versionqueue", + name="title", + field=models.CharField(help_text="Your article title", max_length=300), ), ] diff --git a/src/repository/migrations/0027_auto_20220209_1556.py b/src/repository/migrations/0027_auto_20220209_1556.py index 5a3a7ed00f..618031e22d 100644 --- a/src/repository/migrations/0027_auto_20220209_1556.py +++ b/src/repository/migrations/0027_auto_20220209_1556.py @@ -9,58 +9,88 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0062_auto_20220209_1556'), - ('repository', '0026_merge_20211011_0906'), + ("core", "0062_auto_20220209_1556"), + ("repository", "0026_merge_20211011_0906"), ] operations = [ migrations.CreateModel( - name='RepositoryRole', + name="RepositoryRole", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], ), migrations.AddField( - model_name='repository', - name='limit_access_to_submission', - field=models.BooleanField(default=False, help_text='If enabled, users need to request access to submit preprints.'), + model_name="repository", + name="limit_access_to_submission", + field=models.BooleanField( + default=False, + help_text="If enabled, users need to request access to submit preprints.", + ), ), migrations.AddField( - model_name='repository', - name='submission_access_contact', - field=models.EmailField(blank=True, help_text='Will be notified of new submission access requests.', max_length=254, null=True), + model_name="repository", + name="submission_access_contact", + field=models.EmailField( + blank=True, + help_text="Will be notified of new submission access requests.", + max_length=254, + null=True, + ), ), migrations.AddField( - model_name='repository', - name='submission_access_request_text', - field=models.TextField(blank=True, help_text='Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.', null=True), + model_name="repository", + name="submission_access_request_text", + field=models.TextField( + blank=True, + help_text="Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.", + null=True, + ), ), migrations.AlterField( - model_name='preprint', - name='keywords', - field=core.model_utils.M2MOrderedThroughField(blank=True, null=True, through='repository.KeywordPreprint', to='submission.Keyword'), + model_name="preprint", + name="keywords", + field=core.model_utils.M2MOrderedThroughField( + blank=True, + null=True, + through="repository.KeywordPreprint", + to="submission.Keyword", + ), ), migrations.AlterField( - model_name='versionqueue', - name='title', - field=models.CharField(help_text='Your article title', max_length=300), + model_name="versionqueue", + name="title", + field=models.CharField(help_text="Your article title", max_length=300), ), migrations.AddField( - model_name='repositoryrole', - name='repository', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Repository'), + model_name="repositoryrole", + name="repository", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="repository.Repository" + ), ), migrations.AddField( - model_name='repositoryrole', - name='role', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Role'), + model_name="repositoryrole", + name="role", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="core.Role" + ), ), migrations.AddField( - model_name='repositoryrole', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="repositoryrole", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), ), ] diff --git a/src/repository/migrations/0028_merge_20220223_1229.py b/src/repository/migrations/0028_merge_20220223_1229.py index 37695da314..7d4be616d0 100644 --- a/src/repository/migrations/0028_merge_20220223_1229.py +++ b/src/repository/migrations/0028_merge_20220223_1229.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0027_auto_20220107_1213'), - ('repository', '0027_auto_20220209_1556'), + ("repository", "0027_auto_20220107_1213"), + ("repository", "0027_auto_20220209_1556"), ] - operations = [ - ] + operations = [] diff --git a/src/repository/migrations/0029_auto_20220510_1053.py b/src/repository/migrations/0029_auto_20220510_1053.py index c9a1b9a4a1..b44fe2ff1b 100644 --- a/src/repository/migrations/0029_auto_20220510_1053.py +++ b/src/repository/migrations/0029_auto_20220510_1053.py @@ -9,50 +9,110 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('repository', '0028_merge_20220223_1229'), + ("repository", "0028_merge_20220223_1229"), ] operations = [ migrations.CreateModel( - name='Review', + name="Review", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date_assigned', models.DateTimeField(auto_now_add=True, null=True)), - ('date_due', models.DateField(blank=True, null=True, verbose_name='Due date')), - ('date_accepted', models.DateTimeField(blank=True, null=True)), - ('date_completed', models.DateTimeField(blank=True, null=True)), - ('status', models.CharField(choices=[('new', 'New'), ('accepted', 'Accepted'), ('declined', 'Declined'), ('complete', 'Complete'), ('withdrawn', 'Withdrawn')], max_length=10)), - ('access_code', models.UUIDField(default=uuid.uuid4)), - ('anonymous', models.BooleanField(default=False)), - ('status_reason', models.TextField(blank=True, help_text='Information supplied by a reviewer when declining or completing a review or by staff withdrawing a review', null=True)), - ('notification_sent', models.BooleanField(default=False)), - ('comment', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='repository.Comment')), - ('manager', models.ForeignKey(help_text='The manager making the review request.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='review_manager', to=settings.AUTH_USER_MODEL)), - ('preprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.Preprint')), - ('reviewer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='review_reviewer', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date_assigned", models.DateTimeField(auto_now_add=True, null=True)), + ( + "date_due", + models.DateField(blank=True, null=True, verbose_name="Due date"), + ), + ("date_accepted", models.DateTimeField(blank=True, null=True)), + ("date_completed", models.DateTimeField(blank=True, null=True)), + ( + "status", + models.CharField( + choices=[ + ("new", "New"), + ("accepted", "Accepted"), + ("declined", "Declined"), + ("complete", "Complete"), + ("withdrawn", "Withdrawn"), + ], + max_length=10, + ), + ), + ("access_code", models.UUIDField(default=uuid.uuid4)), + ("anonymous", models.BooleanField(default=False)), + ( + "status_reason", + models.TextField( + blank=True, + help_text="Information supplied by a reviewer when declining or completing a review or by staff withdrawing a review", + null=True, + ), + ), + ("notification_sent", models.BooleanField(default=False)), + ( + "comment", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="repository.Comment", + ), + ), + ( + "manager", + models.ForeignKey( + help_text="The manager making the review request.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="review_manager", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "preprint", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.Preprint", + ), + ), + ( + "reviewer", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="review_reviewer", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='repository', - name='manager_review_status_change', + model_name="repository", + name="manager_review_status_change", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='repository', - name='review_helper', + model_name="repository", + name="review_helper", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='repository', - name='review_invitation', + model_name="repository", + name="review_invitation", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='repository', - name='reviewer_review_status_change', + model_name="repository", + name="reviewer_review_status_change", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/repository/migrations/0029_preprint_article.py b/src/repository/migrations/0029_preprint_article.py index 1d9e9d9a88..7ad9306b8e 100644 --- a/src/repository/migrations/0029_preprint_article.py +++ b/src/repository/migrations/0029_preprint_article.py @@ -7,16 +7,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0068_merge_20220427_1042'), - ('repository', '0028_merge_20220223_1229'), + ("submission", "0068_merge_20220427_1042"), + ("repository", "0028_merge_20220223_1229"), ] operations = [ migrations.AddField( - model_name='preprint', - name='article', - field=models.OneToOneField(blank=True, help_text='Linked article of this preprint.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Article'), + model_name="preprint", + name="article", + field=models.OneToOneField( + blank=True, + help_text="Linked article of this preprint.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Article", + ), ), ] diff --git a/src/repository/migrations/0030_merge_20220613_1628.py b/src/repository/migrations/0030_merge_20220613_1628.py index 16b415ed8f..7db43862d3 100644 --- a/src/repository/migrations/0030_merge_20220613_1628.py +++ b/src/repository/migrations/0030_merge_20220613_1628.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0029_preprint_article'), - ('repository', '0029_auto_20220510_1053'), + ("repository", "0029_preprint_article"), + ("repository", "0029_auto_20220510_1053"), ] - operations = [ - ] + operations = [] diff --git a/src/repository/migrations/0031_repository_active_licenses.py b/src/repository/migrations/0031_repository_active_licenses.py index 7afff82565..3d8e9ffa71 100644 --- a/src/repository/migrations/0031_repository_active_licenses.py +++ b/src/repository/migrations/0031_repository_active_licenses.py @@ -6,16 +6,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0069_delete_blank_keywords'), - ('repository', '0030_merge_20220613_1628'), + ("submission", "0069_delete_blank_keywords"), + ("repository", "0030_merge_20220613_1628"), ] operations = [ migrations.AddField( - model_name='repository', - name='active_licenses', - field=models.ManyToManyField(blank=True, to='submission.Licence'), + model_name="repository", + name="active_licenses", + field=models.ManyToManyField(blank=True, to="submission.Licence"), ), ] diff --git a/src/repository/migrations/0032_repository_submission_notification_recipients.py b/src/repository/migrations/0032_repository_submission_notification_recipients.py index 8475a4429e..86e73b6c72 100644 --- a/src/repository/migrations/0032_repository_submission_notification_recipients.py +++ b/src/repository/migrations/0032_repository_submission_notification_recipients.py @@ -7,16 +7,19 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('repository', '0031_repository_active_licenses'), + ("repository", "0031_repository_active_licenses"), ] operations = [ migrations.AddField( - model_name='repository', - name='submission_notification_recipients', - field=models.ManyToManyField(blank=True, related_name='submission_notification_repositories', to=settings.AUTH_USER_MODEL), + model_name="repository", + name="submission_notification_recipients", + field=models.ManyToManyField( + blank=True, + related_name="submission_notification_repositories", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/repository/migrations/0033_auto_20230120_1546.py b/src/repository/migrations/0033_auto_20230120_1546.py index a15ee27e12..52eafce9ea 100644 --- a/src/repository/migrations/0033_auto_20230120_1546.py +++ b/src/repository/migrations/0033_auto_20230120_1546.py @@ -6,51 +6,78 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_auto_20230120_1546'), + ("core", "0078_auto_20230120_1546"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('press', '0027_auto_20220107_1219'), - ('repository', '0032_repository_submission_notification_recipients'), + ("press", "0027_auto_20220107_1219"), + ("repository", "0032_repository_submission_notification_recipients"), ] operations = [ migrations.AlterModelOptions( - name='preprintaccess', - options={'verbose_name_plural': 'preprint access records'}, + name="preprintaccess", + options={"verbose_name_plural": "preprint access records"}, ), migrations.RemoveField( - model_name='preprintauthor', - name='author', + model_name="preprintauthor", + name="author", ), migrations.AlterField( - model_name='preprintaccess', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.country'), + model_name="preprintaccess", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.country", + ), ), migrations.AlterField( - model_name='preprintaccess', - name='file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.preprintfile'), + model_name="preprintaccess", + name="file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.preprintfile", + ), ), migrations.AlterField( - model_name='preprintauthor', - name='account', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="preprintauthor", + name="account", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='preprintversion', - name='moderated_version', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.versionqueue'), + model_name="preprintversion", + name="moderated_version", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.versionqueue", + ), ), migrations.AlterField( - model_name='repository', - name='press', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='press.press'), + model_name="repository", + name="press", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="press.press", + ), ), migrations.AlterField( - model_name='review', - name='comment', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.comment'), + model_name="review", + name="comment", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.comment", + ), ), ] diff --git a/src/repository/migrations/0033_file_upload_texts.py b/src/repository/migrations/0033_file_upload_texts.py index cf0d1f840a..05d0871307 100644 --- a/src/repository/migrations/0033_file_upload_texts.py +++ b/src/repository/migrations/0033_file_upload_texts.py @@ -6,21 +6,30 @@ class Migration(migrations.Migration): - dependencies = [ - - ('repository', '0032_repository_submission_notification_recipients'), + ("repository", "0032_repository_submission_notification_recipients"), ] operations = [ migrations.AddField( - model_name='repository', - name='file_upload_help', - field=models.TextField(blank=True, help_text='Add any information that the author may need to know as part of the file upload process.', null=True, verbose_name='File Upload Help'), + model_name="repository", + name="file_upload_help", + field=models.TextField( + blank=True, + help_text="Add any information that the author may need to know as part of the file upload process.", + null=True, + verbose_name="File Upload Help", + ), ), migrations.AddField( - model_name='repository', - name='require_pdf_help', - field=models.TextField(blank=True, default='requires that all author uploads be PDF files.', help_text='When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.', null=True, verbose_name='Limit Upload to PDF Help'), + model_name="repository", + name="require_pdf_help", + field=models.TextField( + blank=True, + default="requires that all author uploads be PDF files.", + help_text="When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.", + null=True, + verbose_name="Limit Upload to PDF Help", + ), ), ] diff --git a/src/repository/migrations/0034_alter_preprintaccess_options.py b/src/repository/migrations/0034_alter_preprintaccess_options.py index 61b62e0e14..a36d77b5d6 100644 --- a/src/repository/migrations/0034_alter_preprintaccess_options.py +++ b/src/repository/migrations/0034_alter_preprintaccess_options.py @@ -4,14 +4,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0033_auto_20230120_1546'), + ("repository", "0033_auto_20230120_1546"), ] operations = [ migrations.AlterModelOptions( - name='preprintaccess', + name="preprintaccess", options={}, ), ] diff --git a/src/repository/migrations/0035_alter_preprintaccess_options.py b/src/repository/migrations/0035_alter_preprintaccess_options.py index 146e1f4e4e..36840de299 100644 --- a/src/repository/migrations/0035_alter_preprintaccess_options.py +++ b/src/repository/migrations/0035_alter_preprintaccess_options.py @@ -4,14 +4,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0034_alter_preprintaccess_options'), + ("repository", "0034_alter_preprintaccess_options"), ] operations = [ migrations.AlterModelOptions( - name='preprintaccess', - options={'verbose_name_plural': 'preprint access records'}, + name="preprintaccess", + options={"verbose_name_plural": "preprint access records"}, ), ] diff --git a/src/repository/migrations/0036_merge_20230518_1127.py b/src/repository/migrations/0036_merge_20230518_1127.py index 423507f064..345ebfc92b 100644 --- a/src/repository/migrations/0036_merge_20230518_1127.py +++ b/src/repository/migrations/0036_merge_20230518_1127.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0033_file_upload_texts'), - ('repository', '0035_alter_preprintaccess_options'), + ("repository", "0033_file_upload_texts"), + ("repository", "0035_alter_preprintaccess_options"), ] - operations = [ - ] + operations = [] diff --git a/src/repository/migrations/0036_repository_support_copy_paste.py b/src/repository/migrations/0036_repository_support_copy_paste.py index ddcf9671ec..b80a77f8d5 100644 --- a/src/repository/migrations/0036_repository_support_copy_paste.py +++ b/src/repository/migrations/0036_repository_support_copy_paste.py @@ -5,9 +5,8 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0036_merge_20230518_1127'), + ("repository", "0036_merge_20230518_1127"), ] # Why two operations with support_copy_paste? @@ -19,34 +18,33 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( - model_name='repository', - name='support_copy_paste', + model_name="repository", + name="support_copy_paste", field=models.BooleanField( default=False, ), ), migrations.AlterField( - model_name='repository', - name='support_copy_paste', + model_name="repository", + name="support_copy_paste", field=models.BooleanField( default=True, - help_text='Turn this on if copy-pasting content ' - 'from a word processor, ' - 'or using the toolbar to format text. ' - 'It tells Janeway to clear out formatting ' - 'that does not play nice. ' - 'Turn it off and leave it off if anyone has ' - 'added custom HTML or CSS using the code view, ' - 'since it might remove custom code.', + help_text="Turn this on if copy-pasting content " + "from a word processor, " + "or using the toolbar to format text. " + "It tells Janeway to clear out formatting " + "that does not play nice. " + "Turn it off and leave it off if anyone has " + "added custom HTML or CSS using the code view, " + "since it might remove custom code.", ), ), migrations.AlterField( - model_name='preprint', - name='abstract', + model_name="preprint", + name="abstract", field=django_bleach.models.BleachField( blank=True, - help_text='Copying and pasting from word processors ' - 'is supported.', + help_text="Copying and pasting from word processors " "is supported.", null=True, ), ), diff --git a/src/repository/migrations/0037_historicalrepository.py b/src/repository/migrations/0037_historicalrepository.py index 7baf09cd83..886c613e92 100644 --- a/src/repository/migrations/0037_historicalrepository.py +++ b/src/repository/migrations/0037_historicalrepository.py @@ -7,65 +7,222 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0031_press_journal_footer_text'), + ("press", "0031_press_journal_footer_text"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('repository', '0036_repository_support_copy_paste'), + ("repository", "0036_repository_support_copy_paste"), ] operations = [ migrations.CreateModel( - name='HistoricalRepository', + name="HistoricalRepository", fields=[ - ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), - ('domain', models.CharField(blank=True, db_index=True, max_length=255, null=True)), - ('is_secure', models.BooleanField(default=False, help_text='If the site should redirect to HTTPS, mark this.')), - ('support_copy_paste', models.BooleanField(default=True, help_text='Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.')), - ('name', models.CharField(max_length=255)), - ('short_name', models.CharField(help_text='Shortened version of the name eg. olh. Max 15 chars.', max_length=15)), - ('object_name', models.CharField(help_text='eg. preprint or article', max_length=255)), - ('object_name_plural', models.CharField(help_text='eg. preprints or articles', max_length=255)), - ('logo', models.TextField(blank=True, max_length=100, null=True)), - ('favicon', models.TextField(blank=True, max_length=100, null=True)), - ('hero_background', models.TextField(blank=True, max_length=100, null=True)), - ('publisher', models.CharField(help_text='Used for outputs including DC and Citation metadata', max_length=255)), - ('custom_js_code', models.TextField(blank=True, help_text='The contents of this field are output into the JS areaat the foot of every Repository page.', null=True)), - ('live', models.BooleanField(default=False, verbose_name='Repository is Live?')), - ('limit_upload_to_pdf', models.BooleanField(default=False, help_text='If set to True, this will require all file uploads fromauthors to be PDF files.')), - ('about', models.TextField(blank=True, null=True)), - ('start', models.TextField(blank=True, null=True, verbose_name='Submission Start Text')), - ('file_upload_help', models.TextField(blank=True, help_text='Add any information that the author may need to know as part of the file upload process.', null=True, verbose_name='File Upload Help')), - ('require_pdf_help', models.TextField(blank=True, default='requires that all author uploads be PDF files.', help_text='When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.', null=True, verbose_name='Limit Upload to PDF Help')), - ('submission', models.TextField(blank=True, null=True)), - ('publication', models.TextField(blank=True, null=True)), - ('decline', models.TextField(blank=True, null=True)), - ('accept_version', models.TextField(blank=True, null=True)), - ('decline_version', models.TextField(blank=True, null=True)), - ('new_comment', models.TextField(blank=True, null=True)), - ('review_invitation', models.TextField(blank=True, null=True)), - ('review_helper', models.TextField(blank=True, null=True)), - ('manager_review_status_change', models.TextField(blank=True, null=True)), - ('reviewer_review_status_change', models.TextField(blank=True, null=True)), - ('footer', models.TextField(blank=True, default='

Powered by Janeway

', null=True)), - ('login_text', models.TextField(blank=True, help_text='If text is added it will display on the login and register pages.', null=True, verbose_name='Account Page Text')), - ('submission_agreement', models.TextField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True)), - ('random_homepage_preprints', models.BooleanField(default=False)), - ('limit_access_to_submission', models.BooleanField(default=False, help_text='If enabled, users need to request access to submit preprints.')), - ('submission_access_request_text', models.TextField(blank=True, help_text='Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.', null=True)), - ('submission_access_contact', models.EmailField(blank=True, help_text='Will be notified of new submission access requests.', max_length=254, null=True)), - ('history_id', models.AutoField(primary_key=True, serialize=False)), - ('history_date', models.DateTimeField(db_index=True)), - ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('press', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='press.press')), + ( + "id", + models.IntegerField( + auto_created=True, blank=True, db_index=True, verbose_name="ID" + ), + ), + ( + "domain", + models.CharField( + blank=True, db_index=True, max_length=255, null=True + ), + ), + ( + "is_secure", + models.BooleanField( + default=False, + help_text="If the site should redirect to HTTPS, mark this.", + ), + ), + ( + "support_copy_paste", + models.BooleanField( + default=True, + help_text="Turn this on if copy-pasting content from a word processor, or using the toolbar to format text. It tells Janeway to clear out formatting that does not play nice. Turn it off and leave it off if anyone has added custom HTML or CSS using the code view, since it might remove custom code.", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "short_name", + models.CharField( + help_text="Shortened version of the name eg. olh. Max 15 chars.", + max_length=15, + ), + ), + ( + "object_name", + models.CharField( + help_text="eg. preprint or article", max_length=255 + ), + ), + ( + "object_name_plural", + models.CharField( + help_text="eg. preprints or articles", max_length=255 + ), + ), + ("logo", models.TextField(blank=True, max_length=100, null=True)), + ("favicon", models.TextField(blank=True, max_length=100, null=True)), + ( + "hero_background", + models.TextField(blank=True, max_length=100, null=True), + ), + ( + "publisher", + models.CharField( + help_text="Used for outputs including DC and Citation metadata", + max_length=255, + ), + ), + ( + "custom_js_code", + models.TextField( + blank=True, + help_text="The contents of this field are output into the JS areaat the foot of every Repository page.", + null=True, + ), + ), + ( + "live", + models.BooleanField( + default=False, verbose_name="Repository is Live?" + ), + ), + ( + "limit_upload_to_pdf", + models.BooleanField( + default=False, + help_text="If set to True, this will require all file uploads fromauthors to be PDF files.", + ), + ), + ("about", models.TextField(blank=True, null=True)), + ( + "start", + models.TextField( + blank=True, null=True, verbose_name="Submission Start Text" + ), + ), + ( + "file_upload_help", + models.TextField( + blank=True, + help_text="Add any information that the author may need to know as part of the file upload process.", + null=True, + verbose_name="File Upload Help", + ), + ), + ( + "require_pdf_help", + models.TextField( + blank=True, + default="requires that all author uploads be PDF files.", + help_text="When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.", + null=True, + verbose_name="Limit Upload to PDF Help", + ), + ), + ("submission", models.TextField(blank=True, null=True)), + ("publication", models.TextField(blank=True, null=True)), + ("decline", models.TextField(blank=True, null=True)), + ("accept_version", models.TextField(blank=True, null=True)), + ("decline_version", models.TextField(blank=True, null=True)), + ("new_comment", models.TextField(blank=True, null=True)), + ("review_invitation", models.TextField(blank=True, null=True)), + ("review_helper", models.TextField(blank=True, null=True)), + ( + "manager_review_status_change", + models.TextField(blank=True, null=True), + ), + ( + "reviewer_review_status_change", + models.TextField(blank=True, null=True), + ), + ( + "footer", + models.TextField( + blank=True, default="

Powered by Janeway

", null=True + ), + ), + ( + "login_text", + models.TextField( + blank=True, + help_text="If text is added it will display on the login and register pages.", + null=True, + verbose_name="Account Page Text", + ), + ), + ( + "submission_agreement", + models.TextField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), + ), + ("random_homepage_preprints", models.BooleanField(default=False)), + ( + "limit_access_to_submission", + models.BooleanField( + default=False, + help_text="If enabled, users need to request access to submit preprints.", + ), + ), + ( + "submission_access_request_text", + models.TextField( + blank=True, + help_text="Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.", + null=True, + ), + ), + ( + "submission_access_contact", + models.EmailField( + blank=True, + help_text="Will be notified of new submission access requests.", + max_length=254, + null=True, + ), + ), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ( + "history_type", + models.CharField( + choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], + max_length=1, + ), + ), + ( + "history_user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "press", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="press.press", + ), + ), ], options={ - 'verbose_name': 'historical repository', - 'verbose_name_plural': 'historical repositories', - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': ('history_date', 'history_id'), + "verbose_name": "historical repository", + "verbose_name_plural": "historical repositories", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), }, bases=(simple_history.models.HistoricalChanges, models.Model), ), diff --git a/src/repository/migrations/0039_alter_preprintversion_title.py b/src/repository/migrations/0039_alter_preprintversion_title.py index f3fbbc4802..9904e13074 100644 --- a/src/repository/migrations/0039_alter_preprintversion_title.py +++ b/src/repository/migrations/0039_alter_preprintversion_title.py @@ -4,15 +4,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0037_historicalrepository'), + ("repository", "0037_historicalrepository"), ] operations = [ migrations.AlterField( - model_name='preprintversion', - name='title', - field=models.CharField(blank=True, default='', help_text='Your article title', max_length=300), + model_name="preprintversion", + name="title", + field=models.CharField( + blank=True, default="", help_text="Your article title", max_length=300 + ), ), ] diff --git a/src/repository/migrations/0040_auto_20231207_1002.py b/src/repository/migrations/0040_auto_20231207_1002.py index e459b92100..3717dd403c 100644 --- a/src/repository/migrations/0040_auto_20231207_1002.py +++ b/src/repository/migrations/0040_auto_20231207_1002.py @@ -8,36 +8,42 @@ def set_repo_themes(apps, schema_editor): For existing repositories we should set the theme back to material. """ Repository = apps.get_model( - 'repository', - 'Repository', + "repository", + "Repository", ) - Repository.objects.all().update(theme='material') + Repository.objects.all().update(theme="material") class Migration(migrations.Migration): - dependencies = [ - ('repository', '0039_alter_preprintversion_title'), + ("repository", "0039_alter_preprintversion_title"), ] operations = [ migrations.AddField( - model_name='historicalrepository', - name='theme', - field=models.CharField(choices=[('OLH', 'OLH'), ('material', 'material')], default='OLH', max_length=20), + model_name="historicalrepository", + name="theme", + field=models.CharField( + choices=[("OLH", "OLH"), ("material", "material")], + default="OLH", + max_length=20, + ), ), migrations.AddField( - model_name='repository', - name='theme', - field=models.CharField(choices=[('OLH', 'OLH'), ('material', 'material')], default='OLH', max_length=20), + model_name="repository", + name="theme", + field=models.CharField( + choices=[("OLH", "OLH"), ("material", "material")], + default="OLH", + max_length=20, + ), ), migrations.AlterField( - model_name='preprintversion', - name='title', - field=models.CharField(blank=True, help_text='Your article title', max_length=300), - ), - migrations.RunPython( - set_repo_themes, - reverse_code=migrations.RunPython.noop + model_name="preprintversion", + name="title", + field=models.CharField( + blank=True, help_text="Your article title", max_length=300 + ), ), + migrations.RunPython(set_repo_themes, reverse_code=migrations.RunPython.noop), ] diff --git a/src/repository/migrations/0041_auto_20231207_1658.py b/src/repository/migrations/0041_auto_20231207_1658.py index 69409255ef..e27d051d0e 100644 --- a/src/repository/migrations/0041_auto_20231207_1658.py +++ b/src/repository/migrations/0041_auto_20231207_1658.py @@ -5,6 +5,7 @@ import django.db.models.deletion from django.conf import settings + def add_default_recommendations(apps, schema_editor): ReviewRecommendation = apps.get_model("repository", "ReviewRecommendation") Repository = apps.get_model("repository", "Repository") @@ -12,9 +13,9 @@ def add_default_recommendations(apps, schema_editor): defaults_file = open( os.path.join( settings.BASE_DIR, - 'utils', - 'install', - 'default_repository_review_recommendations.json', + "utils", + "install", + "default_repository_review_recommendations.json", ) ) defaults = json.load(defaults_file) @@ -28,25 +29,44 @@ def add_default_recommendations(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('repository', '0040_auto_20231207_1002'), + ("repository", "0040_auto_20231207_1002"), ] operations = [ migrations.CreateModel( - name='ReviewRecommendation', + name="ReviewRecommendation", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('repository', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repository.repository')), - ('active', models.BooleanField(default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "repository", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="repository.repository", + ), + ), + ("active", models.BooleanField(default=True)), ], ), migrations.AddField( - model_name='review', - name='recommendation', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='repository.reviewrecommendation'), + model_name="review", + name="recommendation", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="repository.reviewrecommendation", + ), + ), + migrations.RunPython( + add_default_recommendations, reverse_code=migrations.RunPython.noop ), - migrations.RunPython(add_default_recommendations, reverse_code=migrations.RunPython.noop) ] diff --git a/src/repository/migrations/0042_auto_20240312_0922.py b/src/repository/migrations/0042_auto_20240312_0922.py index 7cc6d23cbe..3a3cb26633 100644 --- a/src/repository/migrations/0042_auto_20240312_0922.py +++ b/src/repository/migrations/0042_auto_20240312_0922.py @@ -5,233 +5,294 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0041_auto_20231207_1658'), + ("repository", "0041_auto_20231207_1658"), ] operations = [ migrations.RemoveField( - model_name='historicalrepository', - name='support_copy_paste', + model_name="historicalrepository", + name="support_copy_paste", ), migrations.RemoveField( - model_name='repository', - name='support_copy_paste', + model_name="repository", + name="support_copy_paste", ), migrations.AlterField( - model_name='comment', - name='body', - field=core.model_utils.JanewayBleachField(verbose_name='Write your comment:'), + model_name="comment", + name="body", + field=core.model_utils.JanewayBleachField( + verbose_name="Write your comment:" + ), ), migrations.AlterField( - model_name='historicalrepository', - name='about', + model_name="historicalrepository", + name="about", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='accept_version', + model_name="historicalrepository", + name="accept_version", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='decline', + model_name="historicalrepository", + name="decline", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='decline_version', + model_name="historicalrepository", + name="decline_version", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='file_upload_help', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Add any information that the author may need to know as part of the file upload process.', null=True, verbose_name='File Upload Help'), + model_name="historicalrepository", + name="file_upload_help", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Add any information that the author may need to know as part of the file upload process.", + null=True, + verbose_name="File Upload Help", + ), ), migrations.AlterField( - model_name='historicalrepository', - name='footer', - field=core.model_utils.JanewayBleachField(blank=True, default='

Powered by Janeway

', null=True), + model_name="historicalrepository", + name="footer", + field=core.model_utils.JanewayBleachField( + blank=True, default="

Powered by Janeway

", null=True + ), ), migrations.AlterField( - model_name='historicalrepository', - name='login_text', - field=core.model_utils.JanewayBleachField(blank=True, help_text='If text is added it will display on the login and register pages.', null=True, verbose_name='Account Page Text'), + model_name="historicalrepository", + name="login_text", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If text is added it will display on the login and register pages.", + null=True, + verbose_name="Account Page Text", + ), ), migrations.AlterField( - model_name='historicalrepository', - name='manager_review_status_change', + model_name="historicalrepository", + name="manager_review_status_change", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='new_comment', + model_name="historicalrepository", + name="new_comment", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='publication', + model_name="historicalrepository", + name="publication", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='require_pdf_help', - field=core.model_utils.JanewayBleachField(blank=True, default='requires that all author uploads be PDF files.', help_text='When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.', null=True, verbose_name='Limit Upload to PDF Help'), + model_name="historicalrepository", + name="require_pdf_help", + field=core.model_utils.JanewayBleachField( + blank=True, + default="requires that all author uploads be PDF files.", + help_text="When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.", + null=True, + verbose_name="Limit Upload to PDF Help", + ), ), migrations.AlterField( - model_name='historicalrepository', - name='review_helper', + model_name="historicalrepository", + name="review_helper", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='review_invitation', + model_name="historicalrepository", + name="review_invitation", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='reviewer_review_status_change', + model_name="historicalrepository", + name="reviewer_review_status_change", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='start', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Submission Start Text'), + model_name="historicalrepository", + name="start", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Submission Start Text" + ), ), migrations.AlterField( - model_name='historicalrepository', - name='submission', + model_name="historicalrepository", + name="submission", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='historicalrepository', - name='submission_access_request_text', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.', null=True), + model_name="historicalrepository", + name="submission_access_request_text", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.", + null=True, + ), ), migrations.AlterField( - model_name='historicalrepository', - name='submission_agreement', - field=core.model_utils.JanewayBleachField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True), + model_name="historicalrepository", + name="submission_agreement", + field=core.model_utils.JanewayBleachField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), ), migrations.AlterField( - model_name='preprint', - name='abstract', + model_name="preprint", + name="abstract", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='preprint', - name='preprint_decline_note', + model_name="preprint", + name="preprint_decline_note", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='preprintversion', - name='abstract', + model_name="preprintversion", + name="abstract", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='about', + model_name="repository", + name="about", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='accept_version', + model_name="repository", + name="accept_version", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='decline', + model_name="repository", + name="decline", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='decline_version', + model_name="repository", + name="decline_version", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='file_upload_help', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Add any information that the author may need to know as part of the file upload process.', null=True, verbose_name='File Upload Help'), + model_name="repository", + name="file_upload_help", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Add any information that the author may need to know as part of the file upload process.", + null=True, + verbose_name="File Upload Help", + ), ), migrations.AlterField( - model_name='repository', - name='footer', - field=core.model_utils.JanewayBleachField(blank=True, default='

Powered by Janeway

', null=True), + model_name="repository", + name="footer", + field=core.model_utils.JanewayBleachField( + blank=True, default="

Powered by Janeway

", null=True + ), ), migrations.AlterField( - model_name='repository', - name='login_text', - field=core.model_utils.JanewayBleachField(blank=True, help_text='If text is added it will display on the login and register pages.', null=True, verbose_name='Account Page Text'), + model_name="repository", + name="login_text", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If text is added it will display on the login and register pages.", + null=True, + verbose_name="Account Page Text", + ), ), migrations.AlterField( - model_name='repository', - name='manager_review_status_change', + model_name="repository", + name="manager_review_status_change", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='new_comment', + model_name="repository", + name="new_comment", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='publication', + model_name="repository", + name="publication", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='require_pdf_help', - field=core.model_utils.JanewayBleachField(blank=True, default='requires that all author uploads be PDF files.', help_text='When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.', null=True, verbose_name='Limit Upload to PDF Help'), + model_name="repository", + name="require_pdf_help", + field=core.model_utils.JanewayBleachField( + blank=True, + default="requires that all author uploads be PDF files.", + help_text="When a repository requires that all manuscripts be PDF this text is combined with the repository name and displayed with the default text it would diplay: RepositoryName requires that all author uploads be PDF files.", + null=True, + verbose_name="Limit Upload to PDF Help", + ), ), migrations.AlterField( - model_name='repository', - name='review_helper', + model_name="repository", + name="review_helper", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='review_invitation', + model_name="repository", + name="review_invitation", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='reviewer_review_status_change', + model_name="repository", + name="reviewer_review_status_change", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='start', - field=core.model_utils.JanewayBleachField(blank=True, null=True, verbose_name='Submission Start Text'), + model_name="repository", + name="start", + field=core.model_utils.JanewayBleachField( + blank=True, null=True, verbose_name="Submission Start Text" + ), ), migrations.AlterField( - model_name='repository', - name='submission', + model_name="repository", + name="submission", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='repository', - name='submission_access_request_text', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.', null=True), + model_name="repository", + name="submission_access_request_text", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Describe any supporting information you want users to supply when requestingaccess permissions for this repository. Linked to Limit Access to Submissions.", + null=True, + ), ), migrations.AlterField( - model_name='repository', - name='submission_agreement', - field=core.model_utils.JanewayBleachField(default='

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

', help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", null=True), + model_name="repository", + name="submission_agreement", + field=core.model_utils.JanewayBleachField( + default="

Authors grant us the right to publish, on this website, their uploaded manuscript, supplementary materials and any supplied metadata.

", + help_text="Add any information that the author may need to know as part of their submission, eg. Copyright transfer etc.'", + null=True, + ), ), migrations.AlterField( - model_name='repositoryfield', - name='help_text', + model_name="repositoryfield", + name="help_text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='review', - name='status_reason', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Information supplied by a reviewer when declining or completing a review or by staff withdrawing a review', null=True), + model_name="review", + name="status_reason", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Information supplied by a reviewer when declining or completing a review or by staff withdrawing a review", + null=True, + ), ), migrations.AlterField( - model_name='versionqueue', - name='abstract', + model_name="versionqueue", + name="abstract", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), ] diff --git a/src/repository/migrations/0043_auto_20240402_1256.py b/src/repository/migrations/0043_auto_20240402_1256.py index ee0ba82e3b..f1ab64cb12 100644 --- a/src/repository/migrations/0043_auto_20240402_1256.py +++ b/src/repository/migrations/0043_auto_20240402_1256.py @@ -5,20 +5,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0042_auto_20240312_0922'), + ("repository", "0042_auto_20240312_0922"), ] operations = [ migrations.AddField( - model_name='historicalrepository', - name='additional_version_help', - field=core.model_utils.JanewayBleachField(blank=True, default='', help_text='This text allows repository managers to provide additional information to authors when they are uploading an update to their submission.', verbose_name='Additional version upload help text'), + model_name="historicalrepository", + name="additional_version_help", + field=core.model_utils.JanewayBleachField( + blank=True, + default="", + help_text="This text allows repository managers to provide additional information to authors when they are uploading an update to their submission.", + verbose_name="Additional version upload help text", + ), ), migrations.AddField( - model_name='repository', - name='additional_version_help', - field=core.model_utils.JanewayBleachField(blank=True, default='', help_text='This text allows repository managers to provide additional information to authors when they are uploading an update to their submission.', verbose_name='Additional version upload help text'), + model_name="repository", + name="additional_version_help", + field=core.model_utils.JanewayBleachField( + blank=True, + default="", + help_text="This text allows repository managers to provide additional information to authors when they are uploading an update to their submission.", + verbose_name="Additional version upload help text", + ), ), ] diff --git a/src/repository/migrations/0044_alter_repositoryfield_help_text.py b/src/repository/migrations/0044_alter_repositoryfield_help_text.py index dabd63c571..dcc2323e88 100644 --- a/src/repository/migrations/0044_alter_repositoryfield_help_text.py +++ b/src/repository/migrations/0044_alter_repositoryfield_help_text.py @@ -5,7 +5,7 @@ def strip_html_tags_from_repo_field_help_text(apps, schema_editor): - RepositoryField = apps.get_model('repository', 'RepositoryField') + RepositoryField = apps.get_model("repository", "RepositoryField") for field in RepositoryField.objects.all(): field.help_text = striptags(field.help_text) @@ -13,19 +13,18 @@ def strip_html_tags_from_repo_field_help_text(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('repository', '0043_auto_20240402_1256'), + ("repository", "0043_auto_20240402_1256"), ] operations = [ migrations.AlterField( - model_name='repositoryfield', - name='help_text', + model_name="repositoryfield", + name="help_text", field=models.TextField(blank=True, null=True), ), migrations.RunPython( strip_html_tags_from_repo_field_help_text, reverse_code=migrations.RunPython.noop, - ) + ), ] diff --git a/src/repository/migrations/0044_historicalrepository_review_submission_text_and_more.py b/src/repository/migrations/0044_historicalrepository_review_submission_text_and_more.py index cb1da99bbb..4a8fdb1e7b 100644 --- a/src/repository/migrations/0044_historicalrepository_review_submission_text_and_more.py +++ b/src/repository/migrations/0044_historicalrepository_review_submission_text_and_more.py @@ -5,20 +5,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0044_alter_repositoryfield_help_text'), + ("repository", "0044_alter_repositoryfield_help_text"), ] operations = [ migrations.AddField( - model_name='historicalrepository', - name='review_submission_text', - field=core.model_utils.JanewayBleachField(blank=True, default='

Please review your submission carefully. Make any necessary changes to ensure that all information is accurate and complete.

When you are satisfied with your review click the button below to finalize your submission.

', help_text='Text that displays on the review page just before the author completes their submission.'), + model_name="historicalrepository", + name="review_submission_text", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Please review your submission carefully. Make any necessary changes to ensure that all information is accurate and complete.

When you are satisfied with your review click the button below to finalize your submission.

", + help_text="Text that displays on the review page just before the author completes their submission.", + ), ), migrations.AddField( - model_name='repository', - name='review_submission_text', - field=core.model_utils.JanewayBleachField(blank=True, default='

Please review your submission carefully. Make any necessary changes to ensure that all information is accurate and complete.

When you are satisfied with your review click the button below to finalize your submission.

', help_text='Text that displays on the review page just before the author completes their submission.'), + model_name="repository", + name="review_submission_text", + field=core.model_utils.JanewayBleachField( + blank=True, + default="

Please review your submission carefully. Make any necessary changes to ensure that all information is accurate and complete.

When you are satisfied with your review click the button below to finalize your submission.

", + help_text="Text that displays on the review page just before the author completes their submission.", + ), ), ] diff --git a/src/repository/migrations/0045_historicalrepository_display_public_metrics_and_more.py b/src/repository/migrations/0045_historicalrepository_display_public_metrics_and_more.py index 55f11d5de5..f65dd9c0e9 100644 --- a/src/repository/migrations/0045_historicalrepository_display_public_metrics_and_more.py +++ b/src/repository/migrations/0045_historicalrepository_display_public_metrics_and_more.py @@ -4,20 +4,25 @@ class Migration(migrations.Migration): - dependencies = [ - ('repository', '0044_historicalrepository_review_submission_text_and_more'), + ("repository", "0044_historicalrepository_review_submission_text_and_more"), ] operations = [ migrations.AddField( - model_name='historicalrepository', - name='display_public_metrics', - field=models.BooleanField(default=False, help_text='Enable this setting to display metrics publicly.'), + model_name="historicalrepository", + name="display_public_metrics", + field=models.BooleanField( + default=False, + help_text="Enable this setting to display metrics publicly.", + ), ), migrations.AddField( - model_name='repository', - name='display_public_metrics', - field=models.BooleanField(default=False, help_text='Enable this setting to display metrics publicly.'), + model_name="repository", + name="display_public_metrics", + field=models.BooleanField( + default=False, + help_text="Enable this setting to display metrics publicly.", + ), ), ] diff --git a/src/repository/models.py b/src/repository/models.py index 0166f207b7..5042a601a8 100755 --- a/src/repository/models.py +++ b/src/repository/models.py @@ -28,63 +28,61 @@ from events import logic as event_logic -STAGE_PREPRINT_UNSUBMITTED = 'preprint_unsubmitted' -STAGE_PREPRINT_REVIEW = 'preprint_review' -STAGE_PREPRINT_PUBLISHED = 'preprint_published' -STAGE_PREPRINT_REJECTED = 'preprint_rejected' +STAGE_PREPRINT_UNSUBMITTED = "preprint_unsubmitted" +STAGE_PREPRINT_REVIEW = "preprint_review" +STAGE_PREPRINT_PUBLISHED = "preprint_published" +STAGE_PREPRINT_REJECTED = "preprint_rejected" SUBMITTED_STAGES = { STAGE_PREPRINT_REVIEW, STAGE_PREPRINT_PUBLISHED, - STAGE_PREPRINT_REJECTED + STAGE_PREPRINT_REJECTED, } def html_input_types(): return ( - ('text', 'Text'), - ('select', 'Dropdown'), - ('checkbox', 'Checkbox'), - ('number', 'Number'), - ('date', 'Date'), - ('textarea', 'Text Area'), + ("text", "Text"), + ("select", "Dropdown"), + ("checkbox", "Checkbox"), + ("number", "Number"), + ("date", "Date"), + ("textarea", "Text Area"), ) def width_choices(): return ( - (3, '3'), - (6, '6'), - (9, '9'), - (12, '12'), + (3, "3"), + (6, "6"), + (9, "9"), + (12, "12"), ) def theme_choices(): - return( - (theme, theme) for theme in settings.REPOSITORY_THEMES - ) + return ((theme, theme) for theme in settings.REPOSITORY_THEMES) -fs_path = os.path.join('files/') +fs_path = os.path.join("files/") preprint_file_store = JanewayFileSystemStorage(location=fs_path) preprint_media_store = JanewayFileSystemStorage() def preprint_file_upload(instance, filename): try: - uuid_filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + uuid_filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: uuid_filename = str(uuid.uuid4()) - path = os.path.join('repos', str(instance.preprint.pk), uuid_filename) + path = os.path.join("repos", str(instance.preprint.pk), uuid_filename) instance.original_filename = filename return path def repo_media_upload(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -94,28 +92,27 @@ def repo_media_upload(instance, filename): class Repository(model_utils.AbstractSiteModel): press = models.ForeignKey( - 'press.Press', + "press.Press", null=True, on_delete=models.SET_NULL, ) name = models.CharField(max_length=255) short_name = models.CharField( - max_length=15, - help_text='Shortened version of the name eg. olh. Max 15 chars.' + max_length=15, help_text="Shortened version of the name eg. olh. Max 15 chars." ) object_name = models.CharField( max_length=255, - help_text='eg. preprint or article', + help_text="eg. preprint or article", ) object_name_plural = models.CharField( max_length=255, - help_text='eg. preprints or articles', + help_text="eg. preprints or articles", ) - managers = models.ManyToManyField('core.Account', blank=True) + managers = models.ManyToManyField("core.Account", blank=True) submission_notification_recipients = models.ManyToManyField( - 'core.Account', + "core.Account", blank=True, - related_name='submission_notification_repositories', + related_name="submission_notification_repositories", ) logo = model_utils.SVGImageField( blank=True, @@ -137,52 +134,53 @@ class Repository(model_utils.AbstractSiteModel): ) publisher = models.CharField( max_length=255, - help_text=_('Used for outputs including DC and Citation metadata'), + help_text=_("Used for outputs including DC and Citation metadata"), ) custom_js_code = models.TextField( blank=True, null=True, - help_text=_('The contents of this field are output into the JS area' - 'at the foot of every Repository page.') - ) - live = models.BooleanField( - default=False, - verbose_name='Repository is Live?' + help_text=_( + "The contents of this field are output into the JS area" + "at the foot of every Repository page." + ), ) + live = models.BooleanField(default=False, verbose_name="Repository is Live?") limit_upload_to_pdf = models.BooleanField( default=False, - help_text=_('If set to True, this will require all file uploads from' - 'authors to be PDF files.') + help_text=_( + "If set to True, this will require all file uploads from" + "authors to be PDF files." + ), ) about = model_utils.JanewayBleachField(blank=True, null=True) start = model_utils.JanewayBleachField( blank=True, null=True, - verbose_name='Submission Start Text', + verbose_name="Submission Start Text", ) file_upload_help = model_utils.JanewayBleachField( null=True, blank=True, help_text="Add any information that the author may need to know as " - "part of the file upload process.", + "part of the file upload process.", verbose_name="File Upload Help", ) require_pdf_help = model_utils.JanewayBleachField( - default='requires that all author uploads be PDF files.', - help_text='When a repository requires that all manuscripts be PDF this text is combined with the repository ' - 'name and displayed with the default text it would diplay: RepositoryName requires that all author ' - 'uploads be PDF files.', + default="requires that all author uploads be PDF files.", + help_text="When a repository requires that all manuscripts be PDF this text is combined with the repository " + "name and displayed with the default text it would diplay: RepositoryName requires that all author " + "uploads be PDF files.", verbose_name="Limit Upload to PDF Help", null=True, blank=True, ) additional_version_help = model_utils.JanewayBleachField( blank=True, - help_text='This text allows repository managers to provide additional ' - 'information to authors when they are uploading an update ' - 'to their submission.', - default='', - verbose_name="Additional version upload help text" + help_text="This text allows repository managers to provide additional " + "information to authors when they are uploading an update " + "to their submission.", + default="", + verbose_name="Additional version upload help text", ) submission = model_utils.JanewayBleachField(blank=True, null=True) publication = model_utils.JanewayBleachField(blank=True, null=True) @@ -193,75 +191,76 @@ class Repository(model_utils.AbstractSiteModel): review_invitation = model_utils.JanewayBleachField(blank=True, null=True) review_helper = model_utils.JanewayBleachField(blank=True, null=True) manager_review_status_change = model_utils.JanewayBleachField(blank=True, null=True) - reviewer_review_status_change = model_utils.JanewayBleachField(blank=True, null=True) + reviewer_review_status_change = model_utils.JanewayBleachField( + blank=True, null=True + ) footer = model_utils.JanewayBleachField( blank=True, null=True, - default='

Powered by Janeway

', + default="

Powered by Janeway

", ) login_text = model_utils.JanewayBleachField( blank=True, null=True, - help_text='If text is added it will display on the login ' - 'and register pages.', - verbose_name='Account Page Text' + help_text="If text is added it will display on the login " + "and register pages.", + verbose_name="Account Page Text", ) submission_agreement = model_utils.JanewayBleachField( null=True, help_text="Add any information that the author may need to know as " - "part of their submission, eg. Copyright transfer etc.'", + "part of their submission, eg. Copyright transfer etc.'", default="

Authors grant us the right to publish, on this website, " - "their uploaded manuscript, supplementary materials and " - "any supplied metadata.

", + "their uploaded manuscript, supplementary materials and " + "any supplied metadata.

", ) random_homepage_preprints = models.BooleanField(default=False) homepage_preprints = models.ManyToManyField( - 'submission.Article', + "submission.Article", blank=True, ) limit_access_to_submission = models.BooleanField( default=False, - help_text='If enabled, users need to request access to submit preprints.', + help_text="If enabled, users need to request access to submit preprints.", ) submission_access_request_text = model_utils.JanewayBleachField( blank=True, null=True, - help_text='Describe any supporting information you want users to supply when requesting' - 'access permissions for this repository. Linked to Limit Access to Submissions.', + help_text="Describe any supporting information you want users to supply when requesting" + "access permissions for this repository. Linked to Limit Access to Submissions.", ) review_submission_text = model_utils.JanewayBleachField( blank=True, default="

Please review your submission carefully. Make any " - "necessary changes to ensure that all information is accurate " - "and complete.

When you are satisfied with your review " - "click the button below to finalize your submission.

", + "necessary changes to ensure that all information is accurate " + "and complete.

When you are satisfied with your review " + "click the button below to finalize your submission.

", help_text="Text that displays on the review page just before the " - "author completes their submission." + "author completes their submission.", ) submission_access_contact = models.EmailField( blank=True, null=True, - help_text='Will be notified of new submission access requests.', + help_text="Will be notified of new submission access requests.", ) active_licenses = models.ManyToManyField( - 'submission.Licence', + "submission.Licence", blank=True, ) history = HistoricalRecords() theme = models.CharField( max_length=20, blank=False, - default='OLH', + default="OLH", choices=theme_choices(), ) display_public_metrics = models.BooleanField( - default=False, - help_text='Enable this setting to display metrics publicly.' + default=False, help_text="Enable this setting to display metrics publicly." ) class Meta: - verbose_name_plural = 'repositories' + verbose_name_plural = "repositories" @classmethod def get_by_request(cls, request): @@ -269,7 +268,7 @@ def get_by_request(cls, request): if not obj: # Lookup by short_name try: - short_name = request.path.split('/')[1] + short_name = request.path.split("/")[1] obj = cls.objects.get(short_name=short_name) path = short_name except (IndexError, cls.DoesNotExist): @@ -277,8 +276,8 @@ def get_by_request(cls, request): return obj, path def __str__(self): - return '[{}] {}'.format( - 'live' if self.live else 'disabled', + return "[{}] {}".format( + "live" if self.live else "disabled", self.name, ) @@ -286,23 +285,21 @@ def top_level_subjects(self): return Subject.objects.filter( repository=self, parent=None, - ).prefetch_related( - 'children' - ) + ).prefetch_related("children") def additional_submission_fields(self): return RepositoryField.objects.filter( repository=self, ) - def site_url(self, path="", query=''): - if self.domain and not settings.URL_CONFIG == 'path': + def site_url(self, path="", query=""): + if self.domain and not settings.URL_CONFIG == "path": return logic.build_url( - netloc=self.domain, - scheme=self._get_scheme(), - port=None, - path=path, - query=query, + netloc=self.domain, + scheme=self._get_scheme(), + port=None, + path=path, + query=query, ) else: return self.press.site_path_url(self, path, query=query) @@ -314,8 +311,8 @@ def code(self): def reviewer_accounts(self): reviewer_ids = RepositoryRole.objects.filter( repository=self, - role__slug='reviewer', - ).values_list('user__id') + role__slug="reviewer", + ).values_list("user__id") return core_models.Account.objects.filter( pk__in=reviewer_ids, ) @@ -327,16 +324,16 @@ class RepositoryRole(models.Model): on_delete=models.CASCADE, ) user = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) role = models.ForeignKey( - 'core.Role', + "core.Role", on_delete=models.CASCADE, ) def __str__(self): - return 'User {} registered as {} on Repo {}'.format( + return "User {} registered as {} on Repo {}".format( self.user.full_name(), self.role, self.repository.name, @@ -357,7 +354,7 @@ class RepositoryField(models.Model): max_length=1000, null=True, blank=True, - help_text='Separate choices with the bar | character.', + help_text="Separate choices with the bar | character.", ) required = models.BooleanField(default=True) order = models.IntegerField() @@ -367,23 +364,26 @@ class RepositoryField(models.Model): ) display = models.BooleanField( default=False, - help_text='Whether or not display this field in the article page', + help_text="Whether or not display this field in the article page", ) dc_metadata_type = models.CharField( max_length=255, help_text=_( - 'If this field is to be output as a dc metadata field you can add' - 'the type here.' + "If this field is to be output as a dc metadata field you can add" + "the type here." ), blank=True, null=True, ) class Meta: - ordering = ('order', 'name',) + ordering = ( + "order", + "name", + ) def __str__(self): - return '{}: {}'.format(self.repository.name, self.name) + return "{}: {}".format(self.repository.name, self.name) class RepositoryFieldAnswer(models.Model): @@ -394,13 +394,13 @@ class RepositoryFieldAnswer(models.Model): on_delete=models.SET_NULL, ) preprint = models.ForeignKey( - 'Preprint', + "Preprint", on_delete=models.CASCADE, ) answer = models.TextField() def __str__(self): - return '{}: {}'.format(self.preprint, self.answer) + return "{}: {}".format(self.preprint, self.answer) class Preprint(models.Model): @@ -410,23 +410,23 @@ class Preprint(models.Model): on_delete=models.SET_NULL, ) owner = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, - help_text='The account that submitted this item.', + help_text="The account that submitted this item.", ) stage = models.CharField(max_length=25, default=STAGE_PREPRINT_UNSUBMITTED) title = models.CharField( max_length=300, - help_text=_('Your article title'), + help_text=_("Your article title"), ) abstract = model_utils.JanewayBleachField( blank=True, null=True, ) submission_file = models.ForeignKey( - 'PreprintFile', - related_name='submission_file', + "PreprintFile", + related_name="submission_file", blank=True, null=True, on_delete=models.SET_NULL, @@ -438,16 +438,18 @@ class Preprint(models.Model): storage=preprint_media_store, ) subject = models.ManyToManyField( - 'Subject', + "Subject", blank=False, null=True, ) keywords = model_utils.M2MOrderedThroughField( "submission.Keyword", - blank=True, null=True, through='repository.KeywordPreprint', + blank=True, + null=True, + through="repository.KeywordPreprint", ) license = models.ForeignKey( - 'submission.Licence', + "submission.Licence", blank=True, null=True, on_delete=models.SET_NULL, @@ -462,16 +464,16 @@ class Preprint(models.Model): max_length=100, blank=True, null=True, - verbose_name='Published DOI', - help_text='You can add a DOI linking to this item\'s published version using this field. ' - 'Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.' + verbose_name="Published DOI", + help_text="You can add a DOI linking to this item's published version using this field. " + "Please provide the full DOI ie. https://doi.org/10.1017/CBO9781316161012.", ) preprint_doi = models.CharField( max_length=100, blank=True, null=True, - verbose_name='Preprint DOI', - help_text='System supplied DOI. ' + verbose_name="Preprint DOI", + help_text="System supplied DOI. ", ) preprint_decline_note = model_utils.JanewayBleachField( blank=True, @@ -489,15 +491,15 @@ class Preprint(models.Model): current_step = models.IntegerField(default=1) article = models.OneToOneField( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.SET_NULL, - help_text='Linked article of this preprint.', + help_text="Linked article of this preprint.", ) def __str__(self): - return '{}'.format( + return "{}".format( self.title, ) @@ -517,7 +519,8 @@ def current_version(self): def version_files(self): return [ - version.file for version in self.preprintversion_set.filter( + version.file + for version in self.preprintversion_set.filter( Q(moderated_version__approved=True) | Q(moderated_version__isnull=True) ) ] @@ -525,10 +528,7 @@ def version_files(self): @property @cache(300) def views(self): - return PreprintAccess.objects.filter( - preprint=self, - file__isnull=True - ) + return PreprintAccess.objects.filter(preprint=self, file__isnull=True) @property @cache(300) @@ -556,7 +556,7 @@ def next_version_number(self): def authors(self): preprint_authors = PreprintAuthor.objects.filter( preprint=self, - ).select_related('account') + ).select_related("account") return [pa.account for pa in preprint_authors if pa.account] @@ -584,14 +584,16 @@ def display_authors_compact(self): return etal def display_authors(self): - return ", ".join([author.full_name() for author in self.authors if author is not None]) + return ", ".join( + [author.full_name() for author in self.authors if author is not None] + ) def add_user_as_author(self, user): preprint_author, created = PreprintAuthor.objects.get_or_create( account=user, affiliation=user.institution, preprint=self, - defaults={'order': self.next_author_order()}, + defaults={"order": self.next_author_order()}, ) return created @@ -607,12 +609,10 @@ def add_author(self, author): def add_supplementary_file(self, supplementary): return PreprintSupplementaryFile.objects.get_or_create( - label=supplementary.cleaned_data['label'], - url=supplementary.cleaned_data['url'], + label=supplementary.cleaned_data["label"], + url=supplementary.cleaned_data["url"], preprint=self, - defaults={ - 'order': self.next_supp_file_order() - }, + defaults={"order": self.next_supp_file_order()}, ) def next_supp_file_order(self): @@ -696,9 +696,9 @@ def is_published(self): def current_version_file_type(self): if self.current_version.file.mime_type in files.HTML_MIMETYPES: - return 'html' + return "html" elif self.current_version.file.mime_type in files.PDF_MIMETYPES: - return 'pdf' + return "pdf" return None @property @@ -709,13 +709,17 @@ def url(self): @property def local_url(self): url = reverse( - 'repository_preprint', - kwargs={'preprint_id': self.id,} + "repository_preprint", + kwargs={ + "preprint_id": self.id, + }, ) return url - def create_article(self, journal, workflow_stage, journal_license, journal_section, force=False): + def create_article( + self, journal, workflow_stage, journal_license, journal_section, force=False + ): """ Creates an article in a given journal and workflow stage. """ @@ -729,7 +733,7 @@ def create_article(self, journal, workflow_stage, journal_license, journal_secti license=journal_license, section=journal_section, date_submitted=timezone.now(), - comments_editor='Submitted from {}'.format(self.repository.name), + comments_editor="Submitted from {}".format(self.repository.name), stage=workflow_stage, ) @@ -738,9 +742,7 @@ def create_article(self, journal, workflow_stage, journal_license, journal_secti submission_models.ArticleAuthorOrder.objects.get_or_create( article=article, author=preprint_author.account, - defaults={ - 'order': preprint_author.order - } + defaults={"order": preprint_author.order}, ) article.authors.add(preprint_author.account) @@ -776,7 +778,7 @@ class KeywordPreprint(models.Model): class Meta: ordering = ["order"] - unique_together = ('keyword', 'preprint') + unique_together = ("keyword", "preprint") def __str__(self): return self.keyword.word @@ -822,18 +824,18 @@ def path_parts(self): def reverse_kwargs(self): return { - 'preprint_id': self.preprint.pk, - 'file_id': self.pk, + "preprint_id": self.preprint.pk, + "file_id": self.pk, } def download_url(self): return reverse( - 'repository_file_download', + "repository_file_download", kwargs=self.reverse_kwargs(), ) def contents(self): - file = open(self.file.path, mode='r') + file = open(self.file.path, mode="r") contents = file.read() file.close() return contents @@ -845,12 +847,14 @@ class PreprintSupplementaryFile(models.Model): on_delete=models.CASCADE, ) url = models.URLField() - label = models.CharField(max_length=200, verbose_name=_('Label'), default='Supplementary File') + label = models.CharField( + max_length=200, verbose_name=_("Label"), default="Supplementary File" + ) order = models.PositiveIntegerField(default=0) class Meta: - ordering = ('order',) - unique_together = ('url', 'preprint') + ordering = ("order",) + unique_together = ("url", "preprint") class PreprintAccess(models.Model): @@ -867,7 +871,7 @@ class PreprintAccess(models.Model): identifier = models.TextField(blank=True, null=True) accessed = models.DateTimeField(auto_now_add=True) country = models.ForeignKey( - 'core.Country', + "core.Country", blank=True, null=True, on_delete=models.SET_NULL, @@ -876,25 +880,25 @@ class PreprintAccess(models.Model): @property def access_type(self): if self.file: - return 'download' - return 'view' + return "download" + return "view" class Meta: - verbose_name_plural = 'preprint access records' + verbose_name_plural = "preprint access records" class PreprintAuthorManager(models.Manager): def get_queryset(self): - return super().get_queryset().select_related('account') + return super().get_queryset().select_related("account") class PreprintAuthor(models.Model): preprint = models.ForeignKey( - 'Preprint', + "Preprint", on_delete=models.CASCADE, ) account = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -904,21 +908,21 @@ class PreprintAuthor(models.Model): objects = PreprintAuthorManager() class Meta: - ordering = ('order',) - unique_together = ('account', 'preprint') + ordering = ("order",) + unique_together = ("account", "preprint") def __str__(self): - return '{author} linked to {preprint}'.format( - author=self.account.full_name() if self.account else '', + return "{author} linked to {preprint}".format( + author=self.account.full_name() if self.account else "", preprint=self.preprint.title, ) @property def full_name(self): if not self.account.middle_name: - return '{} {}'.format(self.account.first_name, self.account.last_name) + return "{} {}".format(self.account.first_name, self.account.last_name) else: - return '{} {} {}'.format( + return "{} {} {}".format( self.account.first_name, self.account.middle_name, self.account.last_name, @@ -926,9 +930,9 @@ def full_name(self): def dc_name(self): if not self.account.middle_name: - return '{}, {}'.format(self.account.last_name, self.account.first_name) + return "{}, {}".format(self.account.last_name, self.account.first_name) else: - return '{}. {} {}'.format( + return "{}. {} {}".format( self.account.last_name, self.account.first_name, self.account.middle_name, @@ -953,18 +957,15 @@ class Author(models.Model): last_name = models.CharField(max_length=255) affiliation = models.TextField(blank=True, null=True) orcid = models.CharField( - max_length=255, - blank=True, - null=True, - verbose_name=_('ORCID') + max_length=255, blank=True, null=True, verbose_name=_("ORCID") ) @property def full_name(self): if not self.middle_name: - return '{} {}'.format(self.first_name, self.last_name) + return "{} {}".format(self.first_name, self.last_name) else: - return '{} {} {}'.format( + return "{} {} {}".format( self.first_name, self.middle_name, self.last_name, @@ -983,20 +984,19 @@ class PreprintVersion(models.Model): version = models.IntegerField(default=1) date_time = models.DateTimeField(default=timezone.now) moderated_version = models.ForeignKey( - 'VersionQueue', + "VersionQueue", blank=True, null=True, on_delete=models.SET_NULL, ) title = models.CharField( max_length=300, - help_text=_('Your article title'), + help_text=_("Your article title"), blank=True, ) abstract = model_utils.JanewayBleachField( blank=True, null=True, - ) published_doi = models.URLField( max_length=255, @@ -1007,13 +1007,13 @@ class PreprintVersion(models.Model): ) class Meta: - ordering = ('-version', '-date_time', '-id') + ordering = ("-version", "-date_time", "-id") def html(self): if self.file.mime_type in files.HTML_MIMETYPES: return self.file.contents() else: - return '' + return "" @property def safe_title(self): @@ -1023,12 +1023,12 @@ def safe_title(self): return "[Untitled]" def __str__(self): - return f'{self.preprint} (version {self.version})' + return f"{self.preprint} (version {self.version})" class Comment(models.Model): author = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -1038,21 +1038,21 @@ class Comment(models.Model): on_delete=models.SET_NULL, ) reply_to = models.ForeignKey( - 'self', + "self", blank=True, null=True, on_delete=models.SET_NULL, ) date_time = models.DateTimeField(default=timezone.now) - body = model_utils.JanewayBleachField(verbose_name='Write your comment:') + body = model_utils.JanewayBleachField(verbose_name="Write your comment:") is_reviewed = models.BooleanField(default=False) is_public = models.BooleanField(default=False) class Meta: - ordering = ('-date_time', '-pk') + ordering = ("-date_time", "-pk") def __str__(self): - return 'Comment by {author} on {article}'.format( + return "Comment by {author} on {article}".format( author=self.author.full_name(), article=self.preprint.title, ) @@ -1078,21 +1078,21 @@ class Subject(models.Model): ) name = models.CharField(max_length=255) slug = models.SlugField(blank=True, max_length=255) - editors = models.ManyToManyField('core.Account', blank=True) + editors = models.ManyToManyField("core.Account", blank=True) enabled = models.BooleanField( default=True, - help_text='If disabled, this subject will not appear publicly.', + help_text="If disabled, this subject will not appear publicly.", ) parent = models.ForeignKey( - 'self', + "self", blank=True, null=True, - related_name='children', + related_name="children", on_delete=models.SET_NULL, ) class Meta: - ordering = ('slug', 'pk') + ordering = ("slug", "pk") def __str__(self): return self.name @@ -1109,9 +1109,9 @@ def published_preprints_count(self): def version_choices(): return ( - ('correction', 'Text Correction'), - ('metadata_correction', 'Metadata Correction'), - ('version', 'New Version'), + ("correction", "Text Correction"), + ("metadata_correction", "Metadata Correction"), + ("version", "New Version"), ) @@ -1141,7 +1141,7 @@ class VersionQueue(models.Model): title = models.CharField( max_length=300, - help_text=_('Your article title'), + help_text=_("Your article title"), ) abstract = model_utils.JanewayBleachField( blank=True, @@ -1203,11 +1203,11 @@ def decision(self): def status(self): if self.date_decision and self.approved: - return _('Approved') + return _("Approved") elif not self.date_decision: - return _('Under Review') + return _("Under Review") else: - return _('Declined') + return _("Declined") @property def safe_title(self): @@ -1219,17 +1219,17 @@ def safe_title(self): def review_status_choices(): return ( - ('new', 'New'), - ('accepted', 'Accepted'), - ('declined', 'Declined'), - ('complete', 'Complete'), - ('withdrawn', 'Withdrawn'), + ("new", "New"), + ("accepted", "Accepted"), + ("declined", "Declined"), + ("complete", "Complete"), + ("withdrawn", "Withdrawn"), ) class ReviewRecommendation(models.Model): repository = models.ForeignKey( - 'Repository', + "Repository", on_delete=models.CASCADE, ) name = models.CharField( @@ -1245,21 +1245,21 @@ def __str__(self): class Review(models.Model): preprint = models.ForeignKey( - 'Preprint', + "Preprint", on_delete=models.CASCADE, ) manager = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, - related_name='review_manager', - help_text='The manager making the review request.', + related_name="review_manager", + help_text="The manager making the review request.", ) reviewer = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, - related_name='review_reviewer', + related_name="review_reviewer", ) date_assigned = models.DateTimeField( blank=True, @@ -1287,7 +1287,7 @@ class Review(models.Model): default=uuid.uuid4, ) comment = models.OneToOneField( - 'Comment', + "Comment", blank=True, null=True, on_delete=models.SET_NULL, @@ -1298,66 +1298,66 @@ class Review(models.Model): status_reason = model_utils.JanewayBleachField( blank=True, null=True, - help_text='Information supplied by a reviewer when declining or completing ' - 'a review or by staff withdrawing a review', + help_text="Information supplied by a reviewer when declining or completing " + "a review or by staff withdrawing a review", ) notification_sent = models.BooleanField( default=False, ) recommendation = models.ForeignKey( - 'ReviewRecommendation', + "ReviewRecommendation", null=True, on_delete=models.SET_NULL, ) def accept(self, request): self.date_accepted = timezone.now() - self.status = 'accepted' + self.status = "accepted" self.save() # Raise event event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_REVIEW_STATUS_CHANGE, **{ - 'request': request, - 'review': self, - 'status_change': 'accept', - } + "request": request, + "review": self, + "status_change": "accept", + }, ) def decline(self, request): self.date_completed = timezone.now() - self.status = 'declined' + self.status = "declined" self.save() # Raise event event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_REVIEW_STATUS_CHANGE, **{ - 'request': request, - 'review': self, - 'status_change': 'decline', - } + "request": request, + "review": self, + "status_change": "decline", + }, ) def complete(self, request): self.date_completed = timezone.now() - self.status = 'complete' + self.status = "complete" self.save() # Raise event event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_REVIEW_STATUS_CHANGE, **{ - 'request': request, - 'review': self, - 'status_change': 'complete', - } + "request": request, + "review": self, + "status_change": "complete", + }, ) def withdraw(self, reason, request): self.date_completed = timezone.now() - self.status = 'withdrawn' + self.status = "withdrawn" self.status_reason = reason self.save() @@ -1365,23 +1365,23 @@ def withdraw(self, reason, request): event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_REVIEW_STATUS_CHANGE, **{ - 'request': request, - 'review': self, - 'status_change': 'withdraw', - } + "request": request, + "review": self, + "status_change": "withdraw", + }, ) def reset(self, user): self.date_accepted = None self.date_completed = None - self.status = 'new' - self.status_reason = 'Invited Review reset by staff.' + self.status = "new" + self.status_reason = "Invited Review reset by staff." self.save() utils_models.LogEntry.add_entry( - types='Preprint Review', - description='Preprint Review by {} reset'.format(self.reviewer.full_name()), - level='Info', + types="Preprint Review", + description="Preprint Review by {} reset".format(self.reviewer.full_name()), + level="Info", actor=user, target=self.preprint, ) @@ -1393,9 +1393,11 @@ def publish(self, user=None): self.comment.save() utils_models.LogEntry.add_entry( - types='Preprint Review', - description='Preprint Review by {} published'.format(self.reviewer.full_name()), - level='Info', + types="Preprint Review", + description="Preprint Review by {} published".format( + self.reviewer.full_name() + ), + level="Info", actor=user, target=self.preprint, ) @@ -1406,9 +1408,11 @@ def unpublish(self, user): self.comment.save() utils_models.LogEntry.add_entry( - types='Preprint Review', - description='Preprint Review by {} unpublished'.format(self.reviewer.full_name()), - level='Info', + types="Preprint Review", + description="Preprint Review by {} unpublished".format( + self.reviewer.full_name() + ), + level="Info", actor=user, target=self.preprint, ) @@ -1461,9 +1465,9 @@ def add_email_setting_defaults(sender, instance, **kwargs): with open( os.path.join( settings.BASE_DIR, - 'utils', - 'install', - 'default_repository_review_recommendations.json', + "utils", + "install", + "default_repository_review_recommendations.json", ) ) as defaults_file: defaults = json.load(defaults_file) diff --git a/src/repository/tests/test_models.py b/src/repository/tests/test_models.py index ad84c723f1..fd4159d556 100644 --- a/src/repository/tests/test_models.py +++ b/src/repository/tests/test_models.py @@ -28,50 +28,50 @@ def setUp(self): ).first() self.license = sm.Licence.objects.create( journal=self.journal_one, - name='Test License', - short_name='Test', - url='https://janeway.systems', + name="Test License", + short_name="Test", + url="https://janeway.systems", ) self.preprint_author = helpers.create_user( - username='repo_author@janeway.systems', + username="repo_author@janeway.systems", ) self.preprint_one = helpers.create_preprint( self.repository, self.preprint_author, self.subject, - title='Preprint Number One', + title="Preprint Number One", ) self.preprint_two = helpers.create_preprint( self.repository, self.preprint_author, self.subject, - title='Preprint Number Two', + title="Preprint Number Two", ) - @override_settings(BASE_DIR='/tmp/') + @override_settings(BASE_DIR="/tmp/") def test_create_article(self): preprint_file = SimpleUploadedFile( "test1.txt", - b"these are the file contents!" # note the b in front of the string [bytes] + b"these are the file contents!", # note the b in front of the string [bytes] ) - with mock.patch('core.file_system.JanewayFileSystemStorage.location', '/tmp/'): + with mock.patch("core.file_system.JanewayFileSystemStorage.location", "/tmp/"): preprint_one_version_file = rm.PreprintFile.objects.create( preprint=self.preprint_one, file=preprint_file, - original_filename='preprint_file.txt', - mime_type='text/plain', + original_filename="preprint_file.txt", + mime_type="text/plain", size=10000, ) preprint_one_version = rm.PreprintVersion.objects.create( preprint=self.preprint_one, version=1, - title='Preprint Number One', + title="Preprint Number One", file=preprint_one_version_file, ) article = self.preprint_one.create_article( journal=self.journal_one, - workflow_stage='Unassigned', + workflow_stage="Unassigned", journal_license=self.license, journal_section=self.section, ) @@ -89,35 +89,34 @@ def test_create_article(self): article.abstract, ) - def test_create_article_force(self): preprint_file = SimpleUploadedFile( "test1.txt", - b"these are the file contents!" # note the b in front of the string [bytes] + b"these are the file contents!", # note the b in front of the string [bytes] ) - with mock.patch('core.file_system.JanewayFileSystemStorage.location', '/tmp/'): + with mock.patch("core.file_system.JanewayFileSystemStorage.location", "/tmp/"): preprint_one_version_file = rm.PreprintFile.objects.create( preprint=self.preprint_one, file=preprint_file, - original_filename='preprint_file.txt', - mime_type='text/plain', + original_filename="preprint_file.txt", + mime_type="text/plain", size=10000, ) preprint_one_version = rm.PreprintVersion.objects.create( preprint=self.preprint_one, version=1, - title='Preprint Number One', + title="Preprint Number One", file=preprint_one_version_file, ) article_one = self.preprint_one.create_article( journal=self.journal_one, - workflow_stage='Unassigned', + workflow_stage="Unassigned", journal_license=self.license, journal_section=self.section, ) article_two = self.preprint_one.create_article( journal=self.journal_one, - workflow_stage='Unassigned', + workflow_stage="Unassigned", journal_license=self.license, journal_section=self.section, force=True, diff --git a/src/repository/tests/test_views.py b/src/repository/tests/test_views.py index 12982771fa..4511713a82 100644 --- a/src/repository/tests/test_views.py +++ b/src/repository/tests/test_views.py @@ -17,7 +17,10 @@ from dateutil import tz -FROZEN_DATETIME = timezone.datetime(2024, 3, 25, 10, 0, tzinfo=tz.gettz("America/Chicago")) +FROZEN_DATETIME = timezone.datetime( + 2024, 3, 25, 10, 0, tzinfo=tz.gettz("America/Chicago") +) + class TestModels(TestCase): def setUp(self): @@ -25,55 +28,53 @@ def setUp(self): self.press.save() self.request = helpers.Request() self.request.press = self.press - self.repo_manager = helpers.create_user( - 'repo_manager@janeway.systems' - ) + self.repo_manager = helpers.create_user("repo_manager@janeway.systems") self.repo_manager.is_active = True self.repo_manager.save() self.reviewer = helpers.create_user( - 'repo_reviewer@janeway.systems', + "repo_reviewer@janeway.systems", ) self.repository, self.subject = helpers.create_repository( self.press, [self.repo_manager], [], - domain='repo.test.com', + domain="repo.test.com", ) install.load_settings(self.repository) - role = cm.Role.objects.create(name='Reviewer', slug='reviewer') + role = cm.Role.objects.create(name="Reviewer", slug="reviewer") rm.RepositoryRole.objects.create( repository=self.repository, user=self.reviewer, role=role, ) self.preprint_author = helpers.create_user( - username='repo_author@janeway.systems', + username="repo_author@janeway.systems", ) self.preprint_one = helpers.create_preprint( self.repository, self.preprint_author, self.subject, - title='Preprint Number One', + title="Preprint Number One", ) self.recommendation, _ = rm.ReviewRecommendation.objects.get_or_create( repository=self.repository, - name='Accept', + name="Accept", ) self.server_name = "repo.test.com" update_settings() clear_script_prefix() - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_invite_reviewer(self): data = { - 'reviewer': self.reviewer.pk, - 'date_due': '2022-01-01', + "reviewer": self.reviewer.pk, + "date_due": "2022-01-01", } path = reverse( - 'repository_new_review', + "repository_new_review", kwargs={ - 'preprint_id': self.preprint_one.pk, - } + "preprint_id": self.preprint_one.pk, + }, ) self.client.force_login(self.repo_manager) response = self.client.post( @@ -86,7 +87,7 @@ def test_invite_reviewer(self): self.preprint_one.review_set.count(), ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_notify_reviewer(self): review = rm.Review.objects.create( reviewer=self.reviewer, @@ -95,14 +96,14 @@ def test_notify_reviewer(self): date_due=timezone.now(), ) data = { - 'message': 'This is a test email', + "message": "This is a test email", } path = reverse( - 'repository_notify_reviewer', + "repository_notify_reviewer", kwargs={ - 'preprint_id': self.preprint_one.pk, - 'review_id': review.pk, - } + "preprint_id": self.preprint_one.pk, + "review_id": review.pk, + }, ) self.client.force_login( self.repo_manager, @@ -112,31 +113,28 @@ def test_notify_reviewer(self): data=data, SERVER_NAME=self.server_name, ) - self.assertEqual( - 'Preprint Review Invitation', - mail.outbox[0].subject - ) + self.assertEqual("Preprint Review Invitation", mail.outbox[0].subject) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_do_review(self): review = rm.Review.objects.create( reviewer=self.reviewer, preprint=self.preprint_one, manager=self.repo_manager, date_due=timezone.now(), - status='new', + status="new", ) data = { - 'body': 'This is my review.', - 'anonymous': True, - 'recommendation': self.recommendation.pk, + "body": "This is my review.", + "anonymous": True, + "recommendation": self.recommendation.pk, } path = reverse( - 'repository_submit_review', + "repository_submit_review", kwargs={ - 'review_id': review.pk, - 'access_code': review.access_code, - } + "review_id": review.pk, + "access_code": review.access_code, + }, ) self.client.post( path, @@ -149,36 +147,36 @@ def test_do_review(self): ) self.assertEqual( comment.body, - 'This is my review.', + "This is my review.", ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_publish_review_comment(self): comment = rm.Comment.objects.create( author=self.reviewer, preprint=self.preprint_one, - body='This is my review', + body="This is my review", ) review = rm.Review.objects.create( reviewer=self.reviewer, preprint=self.preprint_one, manager=self.repo_manager, date_due=timezone.now(), - status='complete', - comment=comment + status="complete", + comment=comment, ) path = reverse( - 'repository_review_detail', + "repository_review_detail", kwargs={ - 'preprint_id': self.preprint_one.pk, - 'review_id': review.pk, - } + "preprint_id": self.preprint_one.pk, + "review_id": review.pk, + }, ) self.client.force_login(self.repo_manager) self.client.post( path, data={ - 'publish': True, + "publish": True, }, SERVER_NAME=self.server_name, ) @@ -187,15 +185,15 @@ def test_publish_review_comment(self): self.preprint_one.comment_set.filter( review__isnull=False, is_public=True, - ).count() + ).count(), ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_unpublish_review_comment(self): comment = rm.Comment.objects.create( author=self.reviewer, preprint=self.preprint_one, - body='This is my review', + body="This is my review", is_public=True, is_reviewed=True, ) @@ -204,21 +202,21 @@ def test_unpublish_review_comment(self): preprint=self.preprint_one, manager=self.repo_manager, date_due=timezone.now(), - status='complete', - comment=comment + status="complete", + comment=comment, ) path = reverse( - 'repository_review_detail', + "repository_review_detail", kwargs={ - 'preprint_id': self.preprint_one.pk, - 'review_id': review.pk, - } + "preprint_id": self.preprint_one.pk, + "review_id": review.pk, + }, ) self.client.force_login(self.repo_manager) self.client.post( path, data={ - 'unpublish': True, + "unpublish": True, }, SERVER_NAME=self.server_name, ) @@ -227,15 +225,15 @@ def test_unpublish_review_comment(self): self.preprint_one.comment_set.filter( review__isnull=False, is_public=True, - ).count() + ).count(), ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_edit_review_comment(self): comment = rm.Comment.objects.create( author=self.reviewer, preprint=self.preprint_one, - body='This is my review', + body="This is my review", is_public=True, is_reviewed=True, ) @@ -244,24 +242,24 @@ def test_edit_review_comment(self): preprint=self.preprint_one, manager=self.repo_manager, date_due=timezone.now(), - status='complete', + status="complete", comment=comment, recommendation=self.recommendation, ) path = reverse( - 'repository_edit_review_comment', + "repository_edit_review_comment", kwargs={ - 'preprint_id': self.preprint_one.pk, - 'review_id': review.pk, - } + "preprint_id": self.preprint_one.pk, + "review_id": review.pk, + }, ) self.client.force_login(self.repo_manager) self.client.post( path, data={ - 'body': 'This is my slightly different review.', - 'anonymous': False, - 'recommendation': self.recommendation.pk, + "body": "This is my slightly different review.", + "anonymous": False, + "recommendation": self.recommendation.pk, }, SERVER_NAME=self.server_name, ) @@ -271,23 +269,29 @@ def test_edit_review_comment(self): ) self.assertEqual( comment.body, - 'This is my slightly different review.', + "This is my slightly different review.", ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") @freeze_time(FROZEN_DATETIME) def test_accept_preprint(self): self.preprint_one.make_new_version(self.preprint_one.submission_file) - path = reverse('repository_manager_article', - kwargs={'preprint_id': self.preprint_one.pk,}) + path = reverse( + "repository_manager_article", + kwargs={ + "preprint_id": self.preprint_one.pk, + }, + ) self.client.force_login(self.repo_manager) - self.client.post(path, - data={ - 'accept': '', - 'datetime': "2024-03-25 10:00", - 'timezone': "America/Chicago" - }, - SERVER_NAME=self.server_name,) + self.client.post( + path, + data={ + "accept": "", + "datetime": "2024-03-25 10:00", + "timezone": "America/Chicago", + }, + SERVER_NAME=self.server_name, + ) preprint = rm.Preprint.objects.get(pk=self.preprint_one.pk) self.assertEqual( preprint.date_published.timestamp(), @@ -298,20 +302,26 @@ def test_accept_preprint(self): FROZEN_DATETIME.timestamp(), ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") @freeze_time(FROZEN_DATETIME, tz_offset=5) def test_accept_preprint_bad_date(self): self.preprint_one.make_new_version(self.preprint_one.submission_file) - path = reverse('repository_manager_article', - kwargs={'preprint_id': self.preprint_one.pk,}) + path = reverse( + "repository_manager_article", + kwargs={ + "preprint_id": self.preprint_one.pk, + }, + ) self.client.force_login(self.repo_manager) - self.client.post(path, - data={ - 'accept': '', - 'datetime': "2024-35-35 10:00", - 'timezone': "America/Chicago" - }, - SERVER_NAME=self.server_name,) + self.client.post( + path, + data={ + "accept": "", + "datetime": "2024-35-35 10:00", + "timezone": "America/Chicago", + }, + SERVER_NAME=self.server_name, + ) p = rm.Preprint.objects.get(pk=self.preprint_one.pk) self.assertIsNone(p.date_published) self.assertIsNone(p.date_accepted) diff --git a/src/repository/urls.py b/src/repository/urls.py index 1b0a167050..f8251dd254 100755 --- a/src/repository/urls.py +++ b/src/repository/urls.py @@ -9,241 +9,270 @@ from repository import views urlpatterns = [ - re_path(r'^dashboard/$', - views.repository_dashboard, - name='repository_dashboard'), - - re_path(r'^dashboard/(?P\d+)/$', + re_path(r"^dashboard/$", views.repository_dashboard, name="repository_dashboard"), + re_path( + r"^dashboard/(?P\d+)/$", views.repository_author_article, - name='repository_author_article'), - - re_path(r'^dashboard/(?P\d+)/action/(?Pcorrection|version|metadata_correction)/$', + name="repository_author_article", + ), + re_path( + r"^dashboard/(?P\d+)/action/(?Pcorrection|version|metadata_correction)/$", views.repository_submit_update, - name='repository_submit_update'), - - re_path(r'^about/$', - views.repository_about, - name='repository_about'), - - re_path(r'^search/$', + name="repository_submit_update", + ), + re_path(r"^about/$", views.repository_about, name="repository_about"), + re_path(r"^search/$", views.repository_search, name="repository_search"), + re_path( + r"^search/(?P.*)/$", views.repository_search, - name='repository_search'), - - re_path(r'^search/(?P.*)/$', - views.repository_search, - name='repository_search_with_term'), - - re_path(r'^view/(?P\d+)/$', + name="repository_search_with_term", + ), + re_path( + r"^view/(?P\d+)/$", views.repository_preprint, - name='repository_preprint'), - - re_path(r'^view/(?P\d+)/pdf/$', - views.repository_pdf, - name='repository_pdf'), - - re_path(r'^object/(?P\d+)/download/(?P\d+)/$', + name="repository_preprint", + ), + re_path( + r"^view/(?P\d+)/pdf/$", views.repository_pdf, name="repository_pdf" + ), + re_path( + r"^object/(?P\d+)/download/(?P\d+)/$", views.repository_file_download, - name='repository_file_download'), - - re_path(r'^list/$', - views.repository_list, - name='repository_list'), - - re_path(r'^list/subjects/$', + name="repository_file_download", + ), + re_path(r"^list/$", views.repository_list, name="repository_list"), + re_path( + r"^list/subjects/$", views.repository_subject_list, - name='repository_subject_list'), - - re_path(r'^list/(?P\d+)/$', + name="repository_subject_list", + ), + re_path( + r"^list/(?P\d+)/$", views.repository_list, - name='repository_list_subject'), - - re_path(r'^editors/$', - views.preprints_editors, - name='preprints_editors'), - - re_path(r'^submit/start/$', + name="repository_list_subject", + ), + re_path(r"^editors/$", views.preprints_editors, name="preprints_editors"), + re_path(r"^submit/start/$", views.repository_submit, name="repository_submit"), + re_path( + r"^submit/(?P\d+)/$", views.repository_submit, - name='repository_submit'), - - re_path(r'^submit/(?P\d+)/$', - views.repository_submit, - name='repository_submit_with_id'), - - re_path(r'^submit/(?P\d+)/authors/$', + name="repository_submit_with_id", + ), + re_path( + r"^submit/(?P\d+)/authors/$", views.repository_authors, - name='repository_authors'), - - re_path(r'^submit/(?P\d+)/authors/delete/(?P[-\w]+)/$', + name="repository_authors", + ), + re_path( + r"^submit/(?P\d+)/authors/delete/(?P[-\w]+)/$", views.repository_delete_author, - name='repository_delete_author'), - - re_path(r'^submit/(?P\d+)/authors/order/$', + name="repository_delete_author", + ), + re_path( + r"^submit/(?P\d+)/authors/order/$", views.preprints_author_order, - name='preprints_author_order'), - - re_path(r'^submit/(?P\d+)/files/$', + name="preprints_author_order", + ), + re_path( + r"^submit/(?P\d+)/files/$", views.repository_files, - name='repository_files'), - - re_path(r'^submit/(?P\d+)/review/$', + name="repository_files", + ), + re_path( + r"^submit/(?P\d+)/review/$", views.repository_review, - name='repository_review'), - - re_path(r'^manager/$', - views.preprints_manager, - name='preprints_manager'), - - re_path(r'^manager/(?P\d+)/$', + name="repository_review", + ), + re_path(r"^manager/$", views.preprints_manager, name="preprints_manager"), + re_path( + r"^manager/(?P\d+)/$", views.repository_manager_article, - name='repository_manager_article'), - - re_path(r'^manager/(?P\d+)/edit/metadata/$', + name="repository_manager_article", + ), + re_path( + r"^manager/(?P\d+)/edit/metadata/$", views.repository_edit_metadata, - name='repository_edit_metadata'), - re_path(r'^manager/(?P\d+)/edit/authors/(?P\d+)/$', + name="repository_edit_metadata", + ), + re_path( + r"^manager/(?P\d+)/edit/authors/(?P\d+)/$", views.repository_edit_author, - name='repository_edit_authors'), - re_path(r'^manager/(?P\d+)/add/author/$', + name="repository_edit_authors", + ), + re_path( + r"^manager/(?P\d+)/add/author/$", views.repository_edit_author, - name='repository_add_author'), - re_path(r'^manager/(?P\d+)/author/order/$', + name="repository_add_author", + ), + re_path( + r"^manager/(?P\d+)/author/order/$", views.reorder_preprint_authors, - name='repository_manager_order_authors'), - re_path(r'^manager/(?P\d+)/author/delete/$', + name="repository_manager_order_authors", + ), + re_path( + r"^manager/(?P\d+)/author/delete/$", views.delete_preprint_author, - name='repository_manager_delete_author'), - + name="repository_manager_delete_author", + ), # Review - re_path(r'^manager/reviewers/$', + re_path( + r"^manager/reviewers/$", views.manage_reviewers, - name='repository_manage_reviewers'), - re_path(r'^manager/(?P\d+)/review/$', + name="repository_manage_reviewers", + ), + re_path( + r"^manager/(?P\d+)/review/$", views.list_reviews, - name='repository_list_reviews'), - re_path(r'^manager/(?P\d+)/review/new/$', + name="repository_list_reviews", + ), + re_path( + r"^manager/(?P\d+)/review/new/$", views.manage_review, - name='repository_new_review'), - re_path(r'^manager/(?P\d+)/review/(?P\d+)/detail/$', + name="repository_new_review", + ), + re_path( + r"^manager/(?P\d+)/review/(?P\d+)/detail/$", views.review_detail, - name='repository_review_detail'), - re_path(r'^manager/(?P\d+)/review/(?P\d+)/notify/$', + name="repository_review_detail", + ), + re_path( + r"^manager/(?P\d+)/review/(?P\d+)/notify/$", views.notify_reviewer, - name='repository_notify_reviewer'), - re_path(r'^manager/(?P\d+)/review/(?P\d+)/edit_comment/$', + name="repository_notify_reviewer", + ), + re_path( + r"^manager/(?P\d+)/review/(?P\d+)/edit_comment/$", views.edit_review_comment, - name='repository_edit_review_comment'), - - re_path(r'^review/(?P\d+)/access_code/(?P[0-9a-f-]+)/$', + name="repository_edit_review_comment", + ), + re_path( + r"^review/(?P\d+)/access_code/(?P[0-9a-f-]+)/$", views.submit_review, - name='repository_submit_review'), - - re_path(r'^review/(?P\d+)/access_code/(?P[0-9a-f-]+)/download/$', + name="repository_submit_review", + ), + re_path( + r"^review/(?P\d+)/access_code/(?P[0-9a-f-]+)/download/$", views.download_review_file, - name='repository_download_review_file'), - - - - re_path(r'^manager/(?P\d+)/download/(?P\d+)/$', + name="repository_download_review_file", + ), + re_path( + r"^manager/(?P\d+)/download/(?P\d+)/$", views.repository_download_file, - name='repository_download_file'), - - re_path(r'^manager/(?P\d+)/notification/$', + name="repository_download_file", + ), + re_path( + r"^manager/(?P\d+)/notification/$", views.repository_notification, - name='repository_notification'), - - re_path(r'^manager/(?P\d+)/log/$', + name="repository_notification", + ), + re_path( + r"^manager/(?P\d+)/log/$", views.repository_preprint_log, - name='repository_preprint_log'), - - re_path(r'^manager/(?P\d+)/comments/$', + name="repository_preprint_log", + ), + re_path( + r"^manager/(?P\d+)/comments/$", views.repository_comments, - name='repository_comments'), - - re_path(r'^manager/(?P\d+)/supp_files/$', + name="repository_comments", + ), + re_path( + r"^manager/(?P\d+)/supp_files/$", views.manage_supplementary_files, - name='repository_manage_supplementary_files'), - - re_path(r'^manager/(?P\d+)/supp_files/new/$', + name="repository_manage_supplementary_files", + ), + re_path( + r"^manager/(?P\d+)/supp_files/new/$", views.new_supplementary_file, - name='repository_new_supplementary_files'), - - re_path(r'^manager/(?P\d+)/supp_files/order/$', + name="repository_new_supplementary_files", + ), + re_path( + r"^manager/(?P\d+)/supp_files/order/$", views.order_supplementary_files, - name='repository_order_supplementary_files'), - - re_path(r'^manager/(?P\d+)/supp_files/delete/$', + name="repository_order_supplementary_files", + ), + re_path( + r"^manager/(?P\d+)/supp_files/delete/$", views.delete_supplementary_file, - name='repository_delete_supplementary_files'), - - re_path(r'^manager/licenses/$', - views.repository_licenses, - name='repository_licenses'), - - re_path(r'^manager/subjects/$', - views.repository_subjects, - name='repository_subjects'), - - re_path(r'^manager/subjects/delete/$', + name="repository_delete_supplementary_files", + ), + re_path( + r"^manager/licenses/$", views.repository_licenses, name="repository_licenses" + ), + re_path( + r"^manager/subjects/$", views.repository_subjects, name="repository_subjects" + ), + re_path( + r"^manager/subjects/delete/$", views.repository_delete_subject, - name='repository_delete_subject'), - - re_path(r'^manager/subjects/(?P\d+)/$', + name="repository_delete_subject", + ), + re_path( + r"^manager/subjects/(?P\d+)/$", views.repository_subjects, - name='repository_subjects_with_id'), - - re_path(r'^manager/rejected/$', + name="repository_subjects_with_id", + ), + re_path( + r"^manager/rejected/$", views.repository_rejected_submissions, - name='repository_rejected_submissions'), - - re_path(r'^manager/orphans/$', + name="repository_rejected_submissions", + ), + re_path( + r"^manager/orphans/$", views.orphaned_preprints, - name='preprints_orphaned_preprints'), - - re_path(r'^manager/versions/$', - views.version_queue, - name='version_queue'), - - re_path(r'^wizard/$', + name="preprints_orphaned_preprints", + ), + re_path(r"^manager/versions/$", views.version_queue, name="version_queue"), + re_path(r"^wizard/$", views.repository_wizard, name="repository_wizard"), + re_path( + r"^wizard/repository/(?P[-\w]+)/step/(?P\d+)/$", views.repository_wizard, - name='repository_wizard'), - - re_path(r'^wizard/repository/(?P[-\w]+)/step/(?P\d+)/$', - views.repository_wizard, - name='repository_wizard_with_id'), - - re_path(r'^manager/fields/$', - views.repository_fields, - name='repository_fields'), - - re_path(r'^manager/fields/delete/$', + name="repository_wizard_with_id", + ), + re_path(r"^manager/fields/$", views.repository_fields, name="repository_fields"), + re_path( + r"^manager/fields/delete/$", views.repository_delete_field, - name='repository_delete_field'), - - re_path(r'^manager/fields/order/$', + name="repository_delete_field", + ), + re_path( + r"^manager/fields/order/$", views.repository_order_fields, - name='repository_order_fields'), - - re_path(r'^manager/fields/(?P\d+)/$', + name="repository_order_fields", + ), + re_path( + r"^manager/fields/(?P\d+)/$", views.repository_fields, - name='repository_fields_with_id'), - - re_path(r'^manager/(?P\d+)/send_to_journal/$', + name="repository_fields_with_id", + ), + re_path( + r"^manager/(?P\d+)/send_to_journal/$", views.send_preprint_to_journal, - name='repository_send_to_a_journal'), - re_path(r'^manager/(?P\d+)/send_to_journal/(?P\d+)/$', + name="repository_send_to_a_journal", + ), + re_path( + r"^manager/(?P\d+)/send_to_journal/(?P\d+)/$", views.send_preprint_to_journal, - name='repository_send_to_journal'), - re_path(r'^manager/recommendations/$', + name="repository_send_to_journal", + ), + re_path( + r"^manager/recommendations/$", views.list_review_recommendations, - name='repository_list_review_recommendations'), - re_path(r'^manager/recommendations/create/$', + name="repository_list_review_recommendations", + ), + re_path( + r"^manager/recommendations/create/$", views.manage_review_recommendation, - name='repository_create_review_recommendation'), - re_path(r'^manager/recommendations/(?P\d+)/edit/$', + name="repository_create_review_recommendation", + ), + re_path( + r"^manager/recommendations/(?P\d+)/edit/$", views.manage_review_recommendation, - name='repository_edit_review_recommendation'), - + name="repository_edit_review_recommendation", + ), # Popup email - re_path(r'^email/user/(?P\d+)/preprint/(?P\d+)/$', - views.send_user_email, name='send_user_email_preprint'), + re_path( + r"^email/user/(?P\d+)/preprint/(?P\d+)/$", + views.send_user_email, + name="send_user_email_preprint", + ), ] diff --git a/src/repository/views.py b/src/repository/views.py index 9e9fbcefc5..b1ecdcd0f8 100644 --- a/src/repository/views.py +++ b/src/repository/views.py @@ -33,10 +33,10 @@ from utils import ( - logger, - logic as utils_logic, - models as utils_models, - shared as utils_shared, + logger, + logic as utils_logic, + models as utils_models, + shared as utils_shared, ) from events import logic as event_logic from security.decorators import ( @@ -61,17 +61,17 @@ def repository_home(request): repository=request.repository, date_published__lte=timezone.now(), stage=models.STAGE_PREPRINT_PUBLISHED, - ).order_by('-date_published')[:6] + ).order_by("-date_published")[:6] subjects = models.Subject.objects.filter( repository=request.repository, ).prefetch_related( - 'preprint_set', + "preprint_set", ) - template = 'repository/home.html' + template = "repository/home.html" context = { - 'preprints': preprints, - 'subjects': subjects, + "preprints": preprints, + "subjects": subjects, } return render(request, template, context) @@ -91,12 +91,12 @@ def sitemap(request, subject_id=None): ) path_parts = [ request.repository.code, - '{}_sitemap.xml'.format(subject.pk), + "{}_sitemap.xml".format(subject.pk), ] else: path_parts = [ request.repository.code, - 'sitemap.xml', + "sitemap.xml", ] return core_views.sitemap( @@ -123,8 +123,8 @@ def repository_dashboard(request): repository=request.repository, ) - if request.POST and 'delete' in request.POST: - preprint_id = request.POST.get('delete') + if request.POST and "delete" in request.POST: + preprint_id = request.POST.get("delete") if preprint_id: try: preprint = models.Preprint.objects.get( @@ -137,30 +137,30 @@ def repository_dashboard(request): messages.add_message( request, messages.SUCCESS, - '{} deleted.'.format(request.repository.object_name), + "{} deleted.".format(request.repository.object_name), ) except models.Preprint.DoesNotExist: messages.add_message( request, messages.WARNING, - 'No incomplete {} found matching the ID supplied and owned by the current user.'.format( + "No incomplete {} found matching the ID supplied and owned by the current user.".format( request.repository.object_name, - ) + ), ) return redirect( reverse( - 'repository_dashboard', + "repository_dashboard", ) ) - template = 'admin/repository/dashboard.html' + template = "admin/repository/dashboard.html" context = { - 'preprints': preprints, - 'incomplete_preprints': incomplete_preprints, + "preprints": preprints, + "incomplete_preprints": incomplete_preprints, } return render(request, template, context) - + @preprint_editor_or_author_required def repository_submit_update(request, preprint_id, action): @@ -179,8 +179,8 @@ def repository_submit_update(request, preprint_id, action): file_form = None version_form = forms.VersionForm(preprint=preprint) - - if action in ['correction', 'version']: + + if action in ["correction", "version"]: file_form = forms.FileForm(preprint=preprint) if request.POST: @@ -188,7 +188,7 @@ def repository_submit_update(request, preprint_id, action): request.POST, preprint=preprint, ) - if action in ['correction', 'version'] and request.FILES: + if action in ["correction", "version"] and request.FILES: file_form = forms.FileForm( request.POST, request.FILES, @@ -196,12 +196,15 @@ def repository_submit_update(request, preprint_id, action): ) # If required, check if the file is a PDF: if request.repository.limit_upload_to_pdf: - if not files.check_in_memory_mime( - in_memory_file=request.FILES.get('file'), - ) == 'application/pdf': + if ( + not files.check_in_memory_mime( + in_memory_file=request.FILES.get("file"), + ) + == "application/pdf" + ): file_form.add_error( None, - 'You must upload a PDF for your manuscript', + "You must upload a PDF for your manuscript", ) if version_form.is_valid() and (file_form.is_valid() if file_form else True): new_version = version_form.save(commit=False) @@ -215,17 +218,17 @@ def repository_submit_update(request, preprint_id, action): return redirect( reverse( - 'repository_author_article', - kwargs={'preprint_id': preprint.pk}, + "repository_author_article", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'admin/repository/submit_update.html' + template = "admin/repository/submit_update.html" context = { - 'preprint': preprint, - 'action': action, - 'version_form': version_form, - 'file_form': file_form, + "preprint": preprint, + "action": action, + "version_form": version_form, + "file_form": file_form, } return render(request, template, context) @@ -245,19 +248,19 @@ def repository_author_article(request, preprint_id): repository=request.repository, ) - template = 'admin/repository/author_article.html' + template = "admin/repository/author_article.html" context = { - 'preprint': preprint, - 'preprint_journals': repository_logic.get_list_of_preprint_journals(), - 'pending_updates': models.VersionQueue.objects.filter( + "preprint": preprint, + "preprint_journals": repository_logic.get_list_of_preprint_journals(), + "pending_updates": models.VersionQueue.objects.filter( preprint=preprint, date_decision__isnull=True, ), - 'views': models.PreprintAccess.objects.filter( + "views": models.PreprintAccess.objects.filter( preprint=preprint, file__isnull=True, ).count(), - 'downloads': models.PreprintAccess.objects.filter( + "downloads": models.PreprintAccess.objects.filter( preprint=preprint, file__isnull=False, ).count(), @@ -272,7 +275,7 @@ def repository_about(request): :param request: HttpRequest object :return: HttpResponse """ - template = 'repository/about.html' + template = "repository/about.html" return render(request, template, {}) @@ -287,15 +290,14 @@ def repository_subject_list(request): enabled=True, parent__isnull=True, ) - - template = 'repository/list_subjects.html' + + template = "repository/list_subjects.html" context = { - 'top_level_subjects': top_level_subjects, + "top_level_subjects": top_level_subjects, } return render(request, template, context) - def repository_list(request, subject_id=None): """ Displays a list of all published preprints. @@ -311,16 +313,16 @@ def repository_list(request, subject_id=None): preprints = subject.preprint_set.filter( repository=request.repository, date_published__lte=timezone.now(), - ).order_by('-date_published') + ).order_by("-date_published") else: subject = None preprints = models.Preprint.objects.filter( date_published__lte=timezone.now(), repository=request.repository, - ).order_by('-date_published') + ).order_by("-date_published") paginator = Paginator(preprints, 15) - page = request.GET.get('page', 1) + page = request.GET.get("page", 1) try: preprints = paginator.page(page) @@ -329,11 +331,11 @@ def repository_list(request, subject_id=None): except EmptyPage: preprints = paginator.page(paginator.num_pages) - template = 'repository/list.html' + template = "repository/list.html" context = { - 'preprints': preprints, - 'subject': subject, - 'subjects': models.Subject.objects.filter(enabled=True), + "preprints": preprints, + "subject": subject, + "subjects": models.Subject.objects.filter(enabled=True), } return render(request, template, context) @@ -343,12 +345,12 @@ def repository_search(request, search_term=None): """ Searches for and displays a list of Preprints. """ - if request.POST and 'search_term' in request.POST: - search_term = request.POST.get('search_term') + if request.POST and "search_term" in request.POST: + search_term = request.POST.get("search_term") return redirect( reverse( - 'repository_search_with_term', - kwargs={'search_term': search_term}, + "repository_search_with_term", + kwargs={"search_term": search_term}, ) ) @@ -361,40 +363,40 @@ def repository_search(request, search_term=None): if search_term: search_term = search_term.strip() - split_search_term = [ - term.strip() for term in search_term.split(' ') if term - ] + split_search_term = [term.strip() for term in search_term.split(" ") if term] # Initial filter on Title, Abstract and Keywords. preprint_search = preprints.filter( - (Q(title__icontains=search_term) | - Q(abstract__icontains=search_term) | - Q(keywords__word__in=split_search_term)) + ( + Q(title__icontains=search_term) + | Q(abstract__icontains=search_term) + | Q(keywords__word__in=split_search_term) + ) ) from_author = models.PreprintAuthor.objects.filter( ( - Q(account__first_name__in=split_search_term) | - Q(account__middle_name__in=split_search_term) | - Q(account__last_name__in=split_search_term) | - Q(account__institution__icontains=search_term) - ) - & - ( - Q(preprint__repository=request.repository) + Q(account__first_name__in=split_search_term) + | Q(account__middle_name__in=split_search_term) + | Q(account__last_name__in=split_search_term) + | Q(account__institution__icontains=search_term) ) + & (Q(preprint__repository=request.repository)) ) - preprints_from_author = [pa.preprint for pa in models.PreprintAuthor.objects.filter( - pk__in=from_author, - preprint__date_published__lte=timezone.now(), - )] + preprints_from_author = [ + pa.preprint + for pa in models.PreprintAuthor.objects.filter( + pk__in=from_author, + preprint__date_published__lte=timezone.now(), + ) + ] preprints = list(set(list(preprint_search) + preprints_from_author)) - preprints.sort(key=operator.attrgetter('date_published'), reverse=True) + preprints.sort(key=operator.attrgetter("date_published"), reverse=True) paginator = Paginator(preprints, 15) - page = request.GET.get('page', 1) + page = request.GET.get("page", 1) try: preprints = paginator.page(page) @@ -403,10 +405,10 @@ def repository_search(request, search_term=None): except EmptyPage: preprints = paginator.page(paginator.num_pages) - template = 'repository/list.html' + template = "repository/list.html" context = { - 'search_term': search_term, - 'preprints': preprints, + "search_term": search_term, + "preprints": preprints, } return render(request, template, context) @@ -436,9 +438,9 @@ def repository_preprint(request, preprint_id): messages.add_message( request, messages.WARNING, - 'You must be logged in to comment', + "You must be logged in to comment", ) - return redirect(reverse('core_login')) + return redirect(reverse("core_login")) form = forms.CommentForm( request.POST, @@ -451,8 +453,8 @@ def repository_preprint(request, preprint_id): repository_logic.raise_comment_event(request, comment) return redirect( reverse( - 'repository_preprint', - kwargs={'preprint_id': preprint.pk}, + "repository_preprint", + kwargs={"preprint_id": preprint.pk}, ) ) @@ -461,11 +463,11 @@ def repository_preprint(request, preprint_id): preprint, ) - template = 'repository/preprint.html' + template = "repository/preprint.html" context = { - 'preprint': preprint, - 'comments': comments, - 'form': form, + "preprint": preprint, + "comments": comments, + "form": form, } return render(request, template, context) @@ -489,29 +491,24 @@ def repository_file_download(request, preprint_id, file_id): ) if file in preprint.version_files(): - if not request.GET.get('embed'): + if not request.GET.get("embed"): # When the file is embedded we do not count this as a Download. repository_logic.store_preprint_access( request, preprint, file, ) - return files.serve_any_file( - request, - file, - path_parts=(file.path_parts(),) - ) + return files.serve_any_file(request, file, path_parts=(file.path_parts(),)) - raise PermissionDenied('You do not have permission to download this file.') + raise PermissionDenied("You do not have permission to download this file.") def repository_pdf(request, preprint_id): + pdf_url = request.GET.get("file") - pdf_url = request.GET.get('file') - - template = 'common/repository/pdf.html' + template = "common/repository/pdf.html" context = { - 'pdf_url': pdf_url, + "pdf_url": pdf_url, } return render(request, template, context) @@ -528,9 +525,9 @@ def preprints_editors(request): enabled=True, ) - template = 'preprints/editors.html' + template = "preprints/editors.html" context = { - 'subjects': subjects, + "subjects": subjects, } return render(request, template, context) @@ -570,16 +567,16 @@ def repository_submit(request, preprint_id=None): preprint = form.save() return redirect( reverse( - 'repository_authors', - kwargs={'preprint_id': preprint.pk}, + "repository_authors", + kwargs={"preprint_id": preprint.pk}, ), ) - template = 'admin/repository/submit/start.html' + template = "admin/repository/submit/start.html" context = { - 'form': form, - 'preprint': preprint, - 'additional_fields': request.repository.additional_submission_fields(), + "form": form, + "preprint": preprint, + "additional_fields": request.repository.additional_submission_fields(), } return render(request, template, context) @@ -609,8 +606,7 @@ def repository_authors(request, preprint_id): modal, fire_redirect, author_to_add = None, False, None if request.POST: - - if 'self' in request.POST: + if "self" in request.POST: author_preprint_created = preprint.add_user_as_author( request.user, ) @@ -619,18 +615,18 @@ def repository_authors(request, preprint_id): messages.add_message( request, messages.WARNING, - 'This author is already associated with this {}'.format( + "This author is already associated with this {}".format( request.repository.object_name, - ) + ), ) fire_redirect = True - if 'search' in request.POST: + if "search" in request.POST: repository_logic.search_for_authors(request, preprint) fire_redirect = True - if 'form' in request.POST: + if "form" in request.POST: form = forms.AuthorForm( request.POST, request=request, @@ -642,39 +638,36 @@ def repository_authors(request, preprint_id): form.save() fire_redirect = True else: - modal = 'newauthor' + modal = "newauthor" - if 'complete' in request.POST: + if "complete" in request.POST: if preprint.authors: return redirect( - reverse( - 'repository_files', - kwargs={'preprint_id': preprint.pk} - ) + reverse("repository_files", kwargs={"preprint_id": preprint.pk}) ) messages.add_message( request, messages.WARNING, - 'You must add at least one author.', + "You must add at least one author.", ) fire_redirect = True if fire_redirect: return redirect( reverse( - 'repository_authors', + "repository_authors", kwargs={ - 'preprint_id': preprint.pk, - } + "preprint_id": preprint.pk, + }, ) ) - template = 'admin/repository/submit/authors.html' + template = "admin/repository/submit/authors.html" context = { - 'preprint': preprint, - 'form': form, - 'user_is_author': preprint.user_is_author(request.user), - 'modal': modal, + "preprint": preprint, + "form": form, + "user_is_author": preprint.user_is_author(request.user), + "modal": modal, } return render(request, template, context) @@ -704,65 +697,70 @@ def repository_files(request, preprint_id): ) if request.POST: - if request.FILES: - form = forms.FileForm(request.POST, request.FILES, preprint=preprint) - uploaded_file = request.FILES.get('file') + uploaded_file = request.FILES.get("file") # If required, check if the file is a PDF: if request.repository.limit_upload_to_pdf: - if not files.check_in_memory_mime( + if ( + not files.check_in_memory_mime( in_memory_file=uploaded_file, - ) == 'application/pdf': - form.add_error(None, 'You must upload a PDF for your manuscript') + ) + == "application/pdf" + ): + form.add_error(None, "You must upload a PDF for your manuscript") # Check if the form is valid if form.is_valid(): file = form.save() preprint.submission_file = file - preprint.submission_file.original_filename = request.FILES['file'].name + preprint.submission_file.original_filename = request.FILES["file"].name preprint.submission_file.save() preprint.save() - messages.add_message(request, messages.INFO, 'File saved.') + messages.add_message(request, messages.INFO, "File saved.") return redirect( reverse( - 'repository_files', + "repository_files", kwargs={ - 'preprint_id': preprint.pk, + "preprint_id": preprint.pk, }, ) ) - if 'label' and 'url' in request.POST: + if "label" and "url" in request.POST: supplementary = forms.PreprintSupplementaryFileForm( request.POST, preprint=preprint, ) if supplementary.is_valid(): - preprint_supplementary, created = preprint.add_supplementary_file(supplementary) - messages.add_message(request, messages.INFO, 'Supplementary file link saved.') + preprint_supplementary, created = preprint.add_supplementary_file( + supplementary + ) + messages.add_message( + request, messages.INFO, "Supplementary file link saved." + ) - if 'complete' in request.POST: + if "complete" in request.POST: if preprint.submission_file: return redirect( reverse( - 'repository_review', - kwargs={'preprint_id': preprint.pk}, + "repository_review", + kwargs={"preprint_id": preprint.pk}, ) ) else: messages.add_message( request, messages.WARNING, - 'You cannot complete this step without uploading a file.' + "You cannot complete this step without uploading a file.", ) - template = 'admin/repository/submit/files.html' + template = "admin/repository/submit/files.html" context = { - 'preprint': preprint, - 'form': form, - 'supplementary': supplementary, + "preprint": preprint, + "form": form, + "supplementary": supplementary, } return render(request, template, context) @@ -784,9 +782,9 @@ def repository_review(request, preprint_id): repository=request.repository, ) - if request.POST and 'complete' in request.POST: + if request.POST and "complete" in request.POST: preprint.submit_preprint() - kwargs = {'request': request, 'preprint': preprint} + kwargs = {"request": request, "preprint": preprint} event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_SUBMISSION, **kwargs, @@ -794,16 +792,15 @@ def repository_review(request, preprint_id): messages.add_message( request, messages.SUCCESS, - '{object} {title} submitted.'.format( - object=request.repository.object_name, - title=preprint.title - ) + "{object} {title} submitted.".format( + object=request.repository.object_name, title=preprint.title + ), ) - return redirect(reverse('repository_dashboard')) + return redirect(reverse("repository_dashboard")) - template = 'admin/repository/submit/review.html' + template = "admin/repository/submit/review.html" context = { - 'preprint': preprint, + "preprint": preprint, } return render(request, template, context) @@ -843,14 +840,14 @@ def preprints_manager(request): enabled=True, ) - template = 'admin/repository/manager.html' + template = "admin/repository/manager.html" context = { - 'unpublished_preprints': unpublished_preprints, - 'published_preprints': published_preprints, - 'incomplete_preprints': incomplete_preprints, - 'rejected_preprints': rejected_preprints, - 'version_queue': versions, - 'subjects': subjects, + "unpublished_preprints": unpublished_preprints, + "published_preprints": published_preprints, + "incomplete_preprints": incomplete_preprints, + "rejected_preprints": rejected_preprints, + "version_queue": versions, + "subjects": subjects, } return render(request, template, context) @@ -875,24 +872,31 @@ def repository_manager_article(request, preprint_id): redirect_request, modal = False, None if request.POST: - - if 'accept' in request.POST: + if "accept" in request.POST: if not preprint.has_version(): messages.add_message( request, messages.WARNING, - 'You must assign at least one galley file.', + "You must assign at least one galley file.", ) redirect_request = False else: try: - d = datetime.fromisoformat(request.POST.get('datetime', timezone.now().strftime("%Y-%m-%d %H:%M"))) - t = tz.gettz(request.POST.get('timezone', str(timezone.get_current_timezone()))) + d = datetime.fromisoformat( + request.POST.get( + "datetime", timezone.now().strftime("%Y-%m-%d %H:%M") + ) + ) + t = tz.gettz( + request.POST.get( + "timezone", str(timezone.get_current_timezone()) + ) + ) - date_published = datetime(d.year, d.month, d.day, d.hour, d.minute, tzinfo=t) - date_kwargs = { - 'date_published': date_published - } + date_published = datetime( + d.year, d.month, d.day, d.hour, d.minute, tzinfo=t + ) + date_kwargs = {"date_published": date_published} if preprint.date_published: preprint.update_date_published(**date_kwargs) else: @@ -900,14 +904,14 @@ def repository_manager_article(request, preprint_id): event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_PUBLICATION, **{ - 'request': request, - 'preprint': preprint, + "request": request, + "preprint": preprint, }, ) return redirect( reverse( - 'repository_notification', - kwargs={'preprint_id': preprint.pk}, + "repository_notification", + kwargs={"preprint_id": preprint.pk}, ) ) redirect_request = True @@ -920,21 +924,20 @@ def repository_manager_article(request, preprint_id): messages.add_message( request, messages.ERROR, - 'Invalid publication date selected', + "Invalid publication date selected", ) - - if 'decline' in request.POST: - note = request.POST.get('decline_note') + if "decline" in request.POST: + note = request.POST.get("decline_note") preprint.decline(note=note) return redirect( reverse( - 'repository_notification', - kwargs={'preprint_id': preprint.pk}, + "repository_notification", + kwargs={"preprint_id": preprint.pk}, ) ) - if 'upload' in request.POST and request.FILES: + if "upload" in request.POST and request.FILES: file_form = forms.FileForm( request.POST, request.FILES, @@ -943,27 +946,27 @@ def repository_manager_article(request, preprint_id): if file_form.is_valid(): file = file_form.save() - file.original_filename = request.FILES['file'].name + file.original_filename = request.FILES["file"].name file.save() redirect_request = True else: - modal = 'new_file' + modal = "new_file" - if 'delete_file' in request.POST: + if "delete_file" in request.POST: repository_logic.delete_file(request, preprint) redirect_request = True - if 'delete_version' in request.POST: + if "delete_version" in request.POST: repository_logic.handle_delete_version(request, preprint) redirect_request = True - if 'reset' in request.POST: + if "reset" in request.POST: if preprint.date_published or preprint.date_declined: preprint.reset() utils_models.LogEntry.add_entry( - 'Reset', - 'Decision Reset for {}'.format(preprint.title), - 'Info', + "Reset", + "Decision Reset for {}".format(preprint.title), + "Info", request.user, request, preprint, @@ -971,14 +974,14 @@ def repository_manager_article(request, preprint_id): messages.add_message( request, messages.INFO, - 'This preprint has been reset', + "This preprint has been reset", ) redirect_request = True - if 'make_version' in request.POST: + if "make_version" in request.POST: file = get_object_or_404( models.PreprintFile, - pk=request.POST.get('make_version'), + pk=request.POST.get("make_version"), preprint=preprint, ) preprint.make_new_version(file) @@ -987,24 +990,24 @@ def repository_manager_article(request, preprint_id): if redirect_request: return redirect( reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'admin/repository/article.html' + template = "admin/repository/article.html" context = { - 'preprint': preprint, - 'subjects': models.Subject.objects.filter(enabled=True), - 'file_form': file_form, - 'pending_updates': models.VersionQueue.objects.filter( + "preprint": preprint, + "subjects": models.Subject.objects.filter(enabled=True), + "file_form": file_form, + "pending_updates": models.VersionQueue.objects.filter( preprint=preprint, date_decision__isnull=True, ), - 'modal': modal, - 'comment_count': preprint.comment_set.filter( + "modal": modal, + "comment_count": preprint.comment_set.filter( review__isnull=True, - ).count() + ).count(), } return render(request, template, context) @@ -1019,9 +1022,7 @@ def repository_edit_metadata(request, preprint_id): :return: HttpReponse or HttpRedirtect """ preprint = get_object_or_404( - models.Preprint, - pk=preprint_id, - repository=request.repository + models.Preprint, pk=preprint_id, repository=request.repository ) metadata_form = forms.PreprintInfo( @@ -1031,7 +1032,7 @@ def repository_edit_metadata(request, preprint_id): ) if request.POST: - if 'metadata' in request.POST: + if "metadata" in request.POST: metadata_form = forms.PreprintInfo( request.POST, instance=preprint, @@ -1044,16 +1045,16 @@ def repository_edit_metadata(request, preprint_id): return redirect( reverse( - 'repository_edit_metadata', - kwargs={'preprint_id': preprint.pk}, + "repository_edit_metadata", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'admin/repository/edit_metadata.html' + template = "admin/repository/edit_metadata.html" context = { - 'preprint': preprint, - 'metadata_form': metadata_form, - 'additional_fields': request.repository.additional_submission_fields(), + "preprint": preprint, + "metadata_form": metadata_form, + "additional_fields": request.repository.additional_submission_fields(), } return render(request, template, context) @@ -1062,15 +1063,17 @@ def repository_edit_metadata(request, preprint_id): @is_article_preprint_editor def repository_edit_author(request, preprint_id, author_id=None): preprint = get_object_or_404( - models.Preprint, - pk=preprint_id, - repository=request.repository + models.Preprint, pk=preprint_id, repository=request.repository + ) + author = ( + get_object_or_404( + models.PreprintAuthor, + pk=author_id, + preprint=preprint, + ) + if author_id + else None ) - author = get_object_or_404( - models.PreprintAuthor, - pk=author_id, - preprint=preprint, - ) if author_id else None form = forms.AuthorForm( instance=author, @@ -1079,8 +1082,7 @@ def repository_edit_author(request, preprint_id, author_id=None): ) if request.POST: - - if 'search' in request.POST: + if "search" in request.POST: author_save = repository_logic.search_for_authors(request, preprint) else: form = forms.AuthorForm( @@ -1094,18 +1096,18 @@ def repository_edit_author(request, preprint_id, author_id=None): messages.add_message( request, messages.SUCCESS, - _('Author information saved'), + _("Author information saved"), ) if author_save: return redirect( - reverse('repository_edit_authors', args=[preprint.pk, author_save.pk]) + reverse("repository_edit_authors", args=[preprint.pk, author_save.pk]) ) - template = 'admin/repository/edit_authors.html' + template = "admin/repository/edit_authors.html" context = { - 'preprint': preprint, - 'form': form, - 'author': author, + "preprint": preprint, + "form": form, + "author": author, } return render(request, template, context) @@ -1131,11 +1133,7 @@ def repository_download_file(request, preprint_id, file_id): preprint=preprint, ) - return files.serve_any_file( - request, - file, - path_parts=(file.path_parts(),) - ) + return files.serve_any_file(request, file, path_parts=(file.path_parts(),)) @is_article_preprint_editor @@ -1160,12 +1158,12 @@ def repository_notification(request, preprint_id): ) if request.POST: - email_content = request.POST.get('email_content', '') + email_content = request.POST.get("email_content", "") kwargs = { - 'request': request, - 'preprint': preprint, - 'email_content': email_content, - 'skip': True if 'skip' in request.POST else False, + "request": request, + "preprint": preprint, + "email_content": email_content, + "skip": True if "skip" in request.POST else False, } event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_NOTIFICATION, @@ -1173,16 +1171,16 @@ def repository_notification(request, preprint_id): ) return redirect( reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'admin/repository/notification.html' + template = "admin/repository/notification.html" context = { - 'action': action, - 'preprint': preprint, - 'email_content': email_content, + "action": action, + "preprint": preprint, + "email_content": email_content, } return render(request, template, context) @@ -1197,16 +1195,14 @@ def repository_preprint_log(request, preprint_id): :return: HttpResponse """ preprint = get_object_or_404( - models.Preprint, - pk=preprint_id, - repository=request.repository + models.Preprint, pk=preprint_id, repository=request.repository ) log_entries = utils_logic.get_log_entries(preprint) - template = 'admin/repository/log.html' + template = "admin/repository/log.html" context = { - 'preprint': preprint, - 'log_entries': log_entries, + "preprint": preprint, + "log_entries": log_entries, } return render(request, template, context) @@ -1230,21 +1226,22 @@ def repository_comments(request, preprint_id): repository_logic.comment_manager_post(request, preprint) return redirect( reverse( - 'repository_comments', kwargs={'preprint_id': preprint.pk}, + "repository_comments", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'admin/repository/comments.html' + template = "admin/repository/comments.html" context = { - 'preprint': preprint, - 'new_comments': preprint.comment_set.filter( + "preprint": preprint, + "new_comments": preprint.comment_set.filter( is_reviewed=False, review__isnull=True, ), - 'old_comments': preprint.comment_set.filter( + "old_comments": preprint.comment_set.filter( is_reviewed=True, review__isnull=True, - ) + ), } return render(request, template, context) @@ -1252,7 +1249,6 @@ def repository_comments(request, preprint_id): @is_repository_manager def repository_subjects(request, subject_id=None): - subject, parent_subject, initial = None, None, {} if subject_id: @@ -1262,13 +1258,13 @@ def repository_subjects(request, subject_id=None): repository=request.repository, ) - if request.GET.get('parent'): + if request.GET.get("parent"): parent_subject = get_object_or_404( models.Subject, - slug=request.GET.get('parent'), + slug=request.GET.get("parent"), repository=request.repository, ) - initial = {'parent': parent_subject} + initial = {"parent": parent_subject} form = forms.SubjectForm( instance=subject, @@ -1287,19 +1283,19 @@ def repository_subjects(request, subject_id=None): form.save() form.save_m2m() utils_shared.clear_cache() - return redirect(reverse('repository_subjects')) + return redirect(reverse("repository_subjects")) top_level_subjects = models.Subject.objects.filter( parent__isnull=True, repository=request.repository, - ).prefetch_related('editors') + ).prefetch_related("editors") - template = 'admin/repository/subjects.html' + template = "admin/repository/subjects.html" context = { - 'top_level_subjects': top_level_subjects, - 'form': form, - 'subject': subject, - 'active_users': core_models.Account.objects.filter(is_active=True), + "top_level_subjects": top_level_subjects, + "form": form, + "subject": subject, + "active_users": core_models.Account.objects.filter(is_active=True), } return render(request, template, context) @@ -1308,7 +1304,7 @@ def repository_subjects(request, subject_id=None): @require_POST @staff_member_required def repository_delete_subject(request): - subject_id = request.POST.get('delete') + subject_id = request.POST.get("delete") subject = get_object_or_404( models.Subject, pk=subject_id, @@ -1319,12 +1315,10 @@ def repository_delete_subject(request): messages.add_message( request, messages.SUCCESS, - 'Subject deleted. Any associated articles will have been orphaned.', + "Subject deleted. Any associated articles will have been orphaned.", ) - return redirect( - reverse('repository_subjects') - ) + return redirect(reverse("repository_subjects")) @is_repository_manager @@ -1341,9 +1335,9 @@ def repository_rejected_submissions(request): repository=request.repository, ) - template = 'admin/repository/rejected_submissions.html' + template = "admin/repository/rejected_submissions.html" context = { - 'rejected_preprints': rejected_preprints, + "rejected_preprints": rejected_preprints, } return render(request, template, context) @@ -1360,9 +1354,9 @@ def orphaned_preprints(request): request.repository, ) - template = 'admin/repository/orphaned_preprints.html' + template = "admin/repository/orphaned_preprints.html" context = { - 'orphaned_preprints': orphaned_preprints, + "orphaned_preprints": orphaned_preprints, } return render(request, template, context) @@ -1382,15 +1376,15 @@ def version_queue(request): duplicates = repository_logic.check_duplicates(version_queue) if request.POST: - if 'approve' in request.POST: + if "approve" in request.POST: return repository_logic.approve_pending_update(request) - elif 'decline' in request.POST: + elif "decline" in request.POST: return repository_logic.decline_pending_update(request) - template = 'admin/repository/version_queue.html' + template = "admin/repository/version_queue.html" context = { - 'version_queue': version_queue, - 'duplicates': duplicates, + "version_queue": version_queue, + "duplicates": duplicates, } return render(request, template, context) @@ -1424,10 +1418,10 @@ def preprints_author_order(request, preprint_id): if not preprint: raise PermissionDenied( - 'Permission Denied. You must be the owner or a repository manager.', + "Permission Denied. You must be the owner or a repository manager.", ) - posted_author_pks = [int(pk) for pk in request.POST.getlist('authors[]')] + posted_author_pks = [int(pk) for pk in request.POST.getlist("authors[]")] preprint_authors = models.PreprintAuthor.objects.filter( preprint=preprint, ) @@ -1437,14 +1431,14 @@ def preprints_author_order(request, preprint_id): author_order, c = models.PreprintAuthor.objects.get_or_create( preprint=preprint, account=preprint_author.account, - defaults={'order': order}, + defaults={"order": order}, ) if not c: author_order.order = order author_order.save() - return HttpResponse('Complete') + return HttpResponse("Complete") @login_required @@ -1456,9 +1450,9 @@ def repository_delete_author(request, preprint_id, redirect_string): :param preprint_id: int, Preprint PK :return: HttpRedirect """ - author_id = request.POST.get('author_id') + author_id = request.POST.get("author_id") - if redirect_string == 'submission': + if redirect_string == "submission": # Checks the user is the owner of the Preprint. preprint = get_object_or_404( models.Preprint, @@ -1486,31 +1480,31 @@ def repository_delete_author(request, preprint_id, redirect_string): messages.add_message( request, messages.INFO, - 'Author removed from {}'.format( + "Author removed from {}".format( request.repository.object_name, - ) + ), ) - if redirect_string == 'submission': + if redirect_string == "submission": return redirect( reverse( - 'repository_authors', + "repository_authors", kwargs={ - 'preprint_id': preprint_id, - } + "preprint_id": preprint_id, + }, ) ) - elif redirect_string == 'manager': + elif redirect_string == "manager": return redirect( reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) ) @staff_member_required -def repository_wizard(request, short_name=None, step='1'): +def repository_wizard(request, short_name=None, step="1"): """ Presents a Wizard for setting up new Repositories. :param request: HttpRequest @@ -1528,15 +1522,15 @@ def repository_wizard(request, short_name=None, step='1'): else: repository = None - if step == '1': + if step == "1": form_type = forms.RepositoryInitial - elif step == '2': + elif step == "2": form_type = forms.RepositorySite - elif step == '3': + elif step == "3": form_type = forms.RepositorySubmission - elif step == '4': + elif step == "4": form_type = forms.RepositoryEmails - elif step == '5': + elif step == "5": form_type = forms.RepositoryLiveForm else: raise Http404 @@ -1555,41 +1549,37 @@ def repository_wizard(request, short_name=None, step='1'): updated_repository = form.save() # If we reach step 4, redirect to the Repo home page. - if step == '5': + if step == "5": messages.add_message( request, messages.SUCCESS, - '{} has been setup.'.format(updated_repository.name) - ) - return redirect( - reverse( - 'core_manager_index' - ) + "{} has been setup.".format(updated_repository.name), ) + return redirect(reverse("core_manager_index")) kwargs = { - 'short_name': updated_repository.short_name, - 'step': step, + "short_name": updated_repository.short_name, + "step": step, } - if 'next' in request.POST: - kwargs['step'] = str(int(step) + 1) + if "next" in request.POST: + kwargs["step"] = str(int(step) + 1) return redirect( reverse( - 'repository_wizard_with_id', + "repository_wizard_with_id", kwargs=kwargs, ) ) - template = 'admin/repository/wizard.html' + template = "admin/repository/wizard.html" context = { - 'repository': repository, - 'form': form, - 'step': step, - 'help_template': 'admin/elements/repository/{step}_help.html'.format( + "repository": repository, + "form": form, + "step": step, + "help_template": "admin/elements/repository/{step}_help.html".format( step=step, - ) + ), } return render(request, template, context) @@ -1626,20 +1616,20 @@ def repository_fields(request, field_id=None): messages.add_message( request, messages.SUCCESS, - 'Field Saved.', + "Field Saved.", ) return redirect( reverse( - 'repository_fields', + "repository_fields", ) ) - template = 'admin/repository/fields.html' + template = "admin/repository/fields.html" context = { - 'field': field, - 'form': form, - 'fields': request.repository.additional_submission_fields(), + "field": field, + "form": form, + "fields": request.repository.additional_submission_fields(), } return render(request, template, context) @@ -1651,34 +1641,28 @@ def repository_delete_field(request): """ Deletes a Repositories field. """ - field_id = request.POST.get('field_to_delete') + field_id = request.POST.get("field_to_delete") field = get_object_or_404( models.RepositoryField, repository=request.repository, pk=field_id, ) field.delete() - messages.add_message( - request, - messages.WARNING, - 'Field Deleted.' - ) + messages.add_message(request, messages.WARNING, "Field Deleted.") - return redirect( - reverse('repository_fields') - ) + return redirect(reverse("repository_fields")) @require_POST @is_repository_manager def repository_order_fields(request): - ids = [int(_id) for _id in request.POST.getlist('fields[]')] + ids = [int(_id) for _id in request.POST.getlist("fields[]")] for field in request.repository.additional_submission_fields(): field.order = ids.index(field.pk) field.save() - return HttpResponse('Ok') + return HttpResponse("Ok") @preprint_editor_or_author_required @@ -1691,13 +1675,16 @@ def manage_supplementary_files(request, preprint_id): form = forms.PreprintSupplementaryFileForm( preprint=preprint, ) - template = 'admin/repository/manage_supp_files.html' - if preprint.owner == request.user and not request.user in request.repository.managers.all(): - template = 'admin/repository/author_supp_files.html' + template = "admin/repository/manage_supp_files.html" + if ( + preprint.owner == request.user + and not request.user in request.repository.managers.all() + ): + template = "admin/repository/author_supp_files.html" context = { - 'preprint': preprint, - 'supplementary_files': preprint.supplementaryfiles, - 'form': form, + "preprint": preprint, + "supplementary_files": preprint.supplementaryfiles, + "form": form, } return render(request, template, context) @@ -1710,7 +1697,7 @@ def new_supplementary_file(request, preprint_id): pk=preprint_id, repository=request.repository, ) - if 'form' in request.POST: + if "form" in request.POST: form = forms.PreprintSupplementaryFileForm( request.POST, preprint=preprint, @@ -1722,24 +1709,24 @@ def new_supplementary_file(request, preprint_id): messages.add_message( request, messages.SUCCESS, - 'New Supplementary File Created', + "New Supplementary File Created", ) else: messages.add_message( request, messages.WARNING, - ' '.join([' '.join(x for x in l) for l in list(form.errors.values())]) + " ".join([" ".join(x for x in l) for l in list(form.errors.values())]), ) else: messages.add_message( request, messages.INFO, - 'No form supplied.', + "No form supplied.", ) return redirect( reverse( - 'repository_manage_supplementary_files', - kwargs={'preprint_id': preprint.pk}, + "repository_manage_supplementary_files", + kwargs={"preprint_id": preprint.pk}, ) ) @@ -1752,14 +1739,14 @@ def order_supplementary_files(request, preprint_id): pk=preprint_id, repository=request.repository, ) - if 'contact[]' in request.POST: - ids = [int(_id) for _id in request.POST.getlist('contact[]')] + if "contact[]" in request.POST: + ids = [int(_id) for _id in request.POST.getlist("contact[]")] for file in preprint.supplementaryfiles: file.order = ids.index(file.pk) file.save() - return HttpResponse('Ok') + return HttpResponse("Ok") @require_POST @@ -1771,10 +1758,10 @@ def delete_supplementary_file(request, preprint_id): repository=request.repository, ) try: - id_to_delete = int(request.POST.get('delete', 0)) + id_to_delete = int(request.POST.get("delete", 0)) except ValueError: raise ValueError( - 'The Supplementary File ID must be an integer.', + "The Supplementary File ID must be an integer.", ) file_to_delete = get_object_or_404( @@ -1786,12 +1773,12 @@ def delete_supplementary_file(request, preprint_id): messages.add_message( request, messages.SUCCESS, - 'Supplementary file deleted', + "Supplementary file deleted", ) return redirect( reverse( - 'repository_manage_supplementary_files', - kwargs={'preprint_id': preprint.pk}, + "repository_manage_supplementary_files", + kwargs={"preprint_id": preprint.pk}, ) ) @@ -1803,16 +1790,16 @@ def reorder_preprint_authors(request, preprint_id): pk=preprint_id, repository=request.repository, ) - posted_author_pks = [int(pk) for pk in request.POST.getlist('authors[]')] + posted_author_pks = [int(pk) for pk in request.POST.getlist("authors[]")] preprint_authors = models.PreprintAuthor.objects.filter( preprint=preprint, ) utils_shared.set_order( objects=preprint_authors, - order_attr_name='order', + order_attr_name="order", pk_list=posted_author_pks, ) - return HttpResponse('Author Order Updated') + return HttpResponse("Author Order Updated") @is_repository_manager @@ -1823,29 +1810,29 @@ def delete_preprint_author(request, preprint_id): repository=request.repository, ) - if 'author_id' in request.POST: + if "author_id" in request.POST: try: author = models.PreprintAuthor.objects.get( preprint=preprint, - pk=request.POST.get('author_id'), + pk=request.POST.get("author_id"), ) author.delete() messages.add_message( request, messages.SUCCESS, - 'Author record deleted.', + "Author record deleted.", ) except models.PreprintAuthor.DoesNotExist: messages.add_message( request, messages.WARNING, - 'No author found.', + "No author found.", ) return redirect( reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) ) @@ -1877,40 +1864,40 @@ def send_preprint_to_journal(request, preprint_id, journal_id=None): if form.is_valid(): article = preprint.create_article( journal=journal, - workflow_stage=form.cleaned_data.get('stage'), - journal_license=form.cleaned_data.get('license'), - journal_section=form.cleaned_data.get('section'), - force=form.cleaned_data.get('force'), + workflow_stage=form.cleaned_data.get("stage"), + journal_license=form.cleaned_data.get("license"), + journal_section=form.cleaned_data.get("section"), + force=form.cleaned_data.get("force"), ) if article: messages.add_message( request, messages.SUCCESS, - 'Article {} created in journal {}'.format( + "Article {} created in journal {}".format( article.pk, journal.name, - ) + ), ) else: messages.add_message( request, messages.WARNING, - 'No article created.', + "No article created.", ) return redirect( reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'repository/send_preprint_to_journal.html' + template = "repository/send_preprint_to_journal.html" context = { - 'preprint': preprint, - 'journal': journal, - 'form': form, - 'journals': journal_models.Journal.objects.all().order_by('code'), + "preprint": preprint, + "journal": journal, + "form": form, + "journals": journal_models.Journal.objects.all().order_by("code"), } return render( request, @@ -1932,18 +1919,15 @@ def list_reviews(request, preprint_id): ) active_reviews = models.Review.objects.filter( preprint=preprint, - ).exclude( - status__in=['declined', 'withdrawn'] - ) + ).exclude(status__in=["declined", "withdrawn"]) inactive_reviews = models.Review.objects.filter( - preprint=preprint, - status__in=['declined', 'withdrawn'] + preprint=preprint, status__in=["declined", "withdrawn"] ) - template = 'repository/review/list_reviews.html' + template = "repository/review/list_reviews.html" context = { - 'preprint': preprint, - 'active_reviews': active_reviews, - 'inactive_reviews': inactive_reviews, + "preprint": preprint, + "active_reviews": active_reviews, + "inactive_reviews": inactive_reviews, } return render( request, @@ -1971,23 +1955,25 @@ def review_detail(request, preprint_id, review_id): if request.POST: fire_redirect = True - if 'reset' in request.POST: + if "reset" in request.POST: review.reset(user=request.user) - messages.add_message(request, messages.SUCCESS, 'Review reset.') - if 'withdraw' in request.POST: - reason = request.POST.get('withdraw_reason', 'No reason supplied.') + messages.add_message(request, messages.SUCCESS, "Review reset.") + if "withdraw" in request.POST: + reason = request.POST.get("withdraw_reason", "No reason supplied.") review.withdraw(reason, request) - messages.add_message(request, messages.SUCCESS, 'Review withdrawn') - if 'accept' in request.POST: + messages.add_message(request, messages.SUCCESS, "Review withdrawn") + if "accept" in request.POST: review.accept(request) - messages.add_message(request, messages.SUCCESS, 'Marked as Accepted') - if 'publish' in request.POST: + messages.add_message(request, messages.SUCCESS, "Marked as Accepted") + if "publish" in request.POST: review.publish(user=request.user) - messages.add_message(request, messages.SUCCESS, 'Review comment published') - if 'unpublish' in request.POST: + messages.add_message(request, messages.SUCCESS, "Review comment published") + if "unpublish" in request.POST: review.unpublish(user=request.user) - messages.add_message(request, messages.SUCCESS, 'Review comment unpublished') - if 'edit' in request.POST: + messages.add_message( + request, messages.SUCCESS, "Review comment unpublished" + ) + if "edit" in request.POST: form = forms.ReviewDueDateForm( request.POST, instance=review, @@ -1997,7 +1983,7 @@ def review_detail(request, preprint_id, review_id): messages.add_message( request, messages.SUCCESS, - 'Due Date updated.', + "Due Date updated.", ) else: fire_redirect = False @@ -2005,19 +1991,19 @@ def review_detail(request, preprint_id, review_id): if fire_redirect: return redirect( reverse( - 'repository_review_detail', + "repository_review_detail", kwargs={ - 'preprint_id': preprint_id, - 'review_id': review.pk, - } + "preprint_id": preprint_id, + "review_id": review.pk, + }, ) ) - template = 'repository/review/review_detail.html' + template = "repository/review/review_detail.html" context = { - 'review': review, - 'preprint': preprint, - 'form': form, + "review": review, + "preprint": preprint, + "form": form, } return render( request, @@ -2051,23 +2037,23 @@ def manage_review(request, preprint_id): messages.add_message( request, messages.SUCCESS, - 'Review saved.', + "Review saved.", ) return redirect( reverse( - 'repository_notify_reviewer', + "repository_notify_reviewer", kwargs={ - 'preprint_id': preprint.pk, - 'review_id': review.pk, + "preprint_id": preprint.pk, + "review_id": review.pk, }, ) ) - template = 'repository/review/manage_review.html' + template = "repository/review/manage_review.html" context = { - 'form': form, - 'preprint': preprint, - 'reviewers': request.repository.reviewer_accounts(), + "form": form, + "preprint": preprint, + "reviewers": request.repository.reviewer_accounts(), } return render( request, @@ -2090,37 +2076,37 @@ def notify_reviewer(request, preprint_id, review_id): ) message = repository_logic.get_review_notification(request, preprint, review) if request.POST: - message = request.POST.get('message') + message = request.POST.get("message") event_logic.Events.raise_event( event_logic.Events.ON_PREPRINT_REVIEW_NOTIFICATION, **{ - 'request': request, - 'preprint': preprint, - 'review': review, - 'message': message, - 'skip': True if 'skip' in request.POST else False, - } + "request": request, + "preprint": preprint, + "review": review, + "message": message, + "skip": True if "skip" in request.POST else False, + }, ) messages.add_message( request, messages.SUCCESS, - 'Invited review notification sent.', + "Invited review notification sent.", ) - if 'skip' not in request.POST: + if "skip" not in request.POST: review.notification_sent = True review.save() return redirect( reverse( - 'repository_list_reviews', - kwargs={'preprint_id': preprint.pk}, + "repository_list_reviews", + kwargs={"preprint_id": preprint.pk}, ) ) - template = 'repository/review/notify_reviewer.html' + template = "repository/review/notify_reviewer.html" context = { - 'preprint': preprint, - 'review': review, - 'message': message, + "preprint": preprint, + "review": review, + "message": message, } return render( request, @@ -2139,21 +2125,21 @@ def submit_review(request, review_id, access_code): models.Review, pk=review_id, access_code=access_code, - status__in=['new', 'accepted'] + status__in=["new", "accepted"], ) form = forms.ReviewCommentForm( review=review, ) if request.POST: fire_redirect = True - if 'accept' in request.POST: + if "accept" in request.POST: review.accept(request) - if 'decline' in request.POST: + if "decline" in request.POST: review.decline(request) messages.add_message( request, messages.INFO, - 'Thanks for letting us know you cannot add a review comment.', + "Thanks for letting us know you cannot add a review comment.", ) else: form = forms.ReviewCommentForm( @@ -2166,7 +2152,7 @@ def submit_review(request, review_id, access_code): messages.add_message( request, messages.SUCCESS, - 'Review saved. Thank you for your contribution', + "Review saved. Thank you for your contribution", ) else: fire_redirect = False @@ -2175,18 +2161,18 @@ def submit_review(request, review_id, access_code): if request.user.is_authenticated: return redirect( reverse( - 'repository_dashboard', + "repository_dashboard", ) ) return redirect( reverse( - 'website_index', + "website_index", ) ) - template = 'repository/review/submit_review.html' + template = "repository/review/submit_review.html" context = { - 'review': review, - 'form': form, + "review": review, + "form": form, } return render( request, @@ -2203,14 +2189,10 @@ def download_review_file(request, review_id, access_code): models.Review, pk=review_id, access_code=access_code, - status__in=['new', 'accepted'] + status__in=["new", "accepted"], ) file = review.preprint.current_version.file - return files.serve_any_file( - request, - file, - path_parts=(file.path_parts(),) - ) + return files.serve_any_file(request, file, path_parts=(file.path_parts(),)) @is_repository_manager @@ -2238,22 +2220,22 @@ def edit_review_comment(request, preprint_id, review_id): messages.add_message( request, messages.SUCCESS, - 'Review comment saved.', + "Review comment saved.", ) return redirect( reverse( - 'repository_review_detail', + "repository_review_detail", kwargs={ - 'preprint_id': preprint.pk, - 'review_id': review.pk, - } + "preprint_id": preprint.pk, + "review_id": review.pk, + }, ) ) - template = 'repository/review/edit_review_comment.html' + template = "repository/review/edit_review_comment.html" context = { - 'preprint': preprint, - 'review': review, - 'form': form, + "preprint": preprint, + "review": review, + "form": form, } return render( request, @@ -2264,30 +2246,33 @@ def edit_review_comment(request, preprint_id, review_id): @is_repository_manager def manage_reviewers(request): - role = core_models.Role.objects.get(slug='reviewer') + role = core_models.Role.objects.get(slug="reviewer") user_search = [] - first_name = request.GET.get('first_name', '') - last_name = request.GET.get('last_name', '') - email = request.GET.get('email', '') + first_name = request.GET.get("first_name", "") + last_name = request.GET.get("last_name", "") + email = request.GET.get("email", "") if first_name or last_name or email: filters = {} if first_name and len(first_name) >= 2: - filters['first_name__icontains'] = first_name + filters["first_name__icontains"] = first_name if last_name and len(last_name) >= 2: - filters['last_name__icontains'] = last_name + filters["last_name__icontains"] = last_name if email and len(email) >= 2: - filters['email__icontains'] = email + filters["email__icontains"] = email user_search = core_models.Account.objects.filter( - **filters, is_active=True, + **filters, + is_active=True, ).exclude( pk__in=request.repository.reviewer_accounts().values("id"), ) - if request.POST and ('add_reviewer' in request.POST or 'remove_reviewer' in request.POST): - if 'add_reviewer' in request.POST: - account_id = request.POST.get('add_reviewer') + if request.POST and ( + "add_reviewer" in request.POST or "remove_reviewer" in request.POST + ): + if "add_reviewer" in request.POST: + account_id = request.POST.get("add_reviewer") account = get_object_or_404( core_models.Account, pk=account_id, @@ -2301,12 +2286,12 @@ def manage_reviewers(request): messages.add_message( request, messages.SUCCESS, - '{} now has the Reviewer role.'.format( + "{} now has the Reviewer role.".format( account.full_name(), - ) + ), ) - if 'remove_reviewer' in request.POST: - account_id = request.POST.get('remove_reviewer') + if "remove_reviewer" in request.POST: + account_id = request.POST.get("remove_reviewer") account = get_object_or_404( core_models.Account, pk=account_id, @@ -2320,23 +2305,23 @@ def manage_reviewers(request): messages.add_message( request, messages.SUCCESS, - '{} no longer has the reviewer role.'.format( + "{} no longer has the reviewer role.".format( account.full_name(), - ) + ), ) return redirect( reverse( - 'repository_manage_reviewers', + "repository_manage_reviewers", ) ) - template = 'repository/review/manage_reviewers.html' + template = "repository/review/manage_reviewers.html" context = { - 'reviewers': request.repository.reviewer_accounts(), - 'user_search': user_search, - 'first_name': first_name, - 'last_name': last_name, - 'email': email, + "reviewers": request.repository.reviewer_accounts(), + "user_search": user_search, + "first_name": first_name, + "last_name": last_name, + "email": email, } return render( request, @@ -2360,19 +2345,15 @@ def repository_licenses(request): ) if form.is_valid(): form.save() - messages.add_message( - request, - messages.SUCCESS, - 'Active Licenses Saved.' - ) + messages.add_message(request, messages.SUCCESS, "Active Licenses Saved.") return redirect( reverse( - 'repository_licenses', + "repository_licenses", ) ) - template = 'admin/repository/licenses.html' + template = "admin/repository/licenses.html" context = { - 'form': form, + "form": form, } return render( request, @@ -2385,8 +2366,7 @@ def repository_licenses(request): def send_user_email(request, user_id, preprint_id): user = get_object_or_404(core_models.Account, pk=user_id) form = core_forms.EmailForm( - initial={'body': '
{signature}'.format( - signature=request.user.signature)}, + initial={"body": "
{signature}".format(signature=request.user.signature)}, ) close, article, preprint = False, None, None preprint = get_object_or_404( @@ -2395,7 +2375,7 @@ def send_user_email(request, user_id, preprint_id): repository=request.repository, ) - if request.POST and 'send' in request.POST: + if request.POST and "send" in request.POST: form = core_forms.EmailForm(request.POST) if form.is_valid(): @@ -2408,12 +2388,12 @@ def send_user_email(request, user_id, preprint_id): ) close = True - template = 'admin/journal/send_user_email.html' + template = "admin/journal/send_user_email.html" context = { - 'user': user, - 'close': close, - 'form': form, - 'article': article, + "user": user, + "close": close, + "form": form, + "article": article, } return render(request, template, context) @@ -2423,8 +2403,8 @@ def list_review_recommendations(request): recommendations = models.ReviewRecommendation.objects.filter( repository=request.repository, ) - if request.POST and 'delete' in request.POST: - recommendation_id = request.POST.get('delete') + if request.POST and "delete" in request.POST: + recommendation_id = request.POST.get("delete") try: recommendation = models.ReviewRecommendation.objects.get( pk=recommendation_id, @@ -2434,29 +2414,25 @@ def list_review_recommendations(request): messages.add_message( request, messages.SUCCESS, - 'Recommendation deleted.', + "Recommendation deleted.", ) else: messages.add_message( request, messages.INFO, - 'Recommendation is linked to reviews. You can mark it as' - ' inactive if you no longer wish to use it.', + "Recommendation is linked to reviews. You can mark it as" + " inactive if you no longer wish to use it.", ) except models.ReviewRecommendation.DoesNotExist: messages.add_message( request, messages.WARNING, - 'No recommendation found with that ID.', - ) - return redirect( - reverse( - 'repository_list_review_recommendations' + "No recommendation found with that ID.", ) - ) - template = 'admin/repository/review/list_review_recommendations.html' + return redirect(reverse("repository_list_review_recommendations")) + template = "admin/repository/review/list_review_recommendations.html" context = { - 'recommendations': recommendations, + "recommendations": recommendations, } return render( request, @@ -2486,15 +2462,11 @@ def manage_review_recommendation(request, recommendation_id=None): ) if form.is_valid(): form.save() - return redirect( - reverse( - 'repository_list_review_recommendations' - ) - ) - template = 'admin/repository/review/manage_review_recommendation.html' + return redirect(reverse("repository_list_review_recommendations")) + template = "admin/repository/review/manage_review_recommendation.html" context = { - 'recommendation': recommendation, - 'form': form, + "recommendation": recommendation, + "form": form, } return render( request, diff --git a/src/review/admin.py b/src/review/admin.py index a61cd16d33..b7e3fd8939 100755 --- a/src/review/admin.py +++ b/src/review/admin.py @@ -10,44 +10,87 @@ class EditorialAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', 'editor', - 'editor_type', 'assigned', 'notified') - list_filter = ('article__journal', 'editor_type', 'assigned', 'notified') - search_fields = ('article__pk', 'article__title', 'article__journal__code', - 'editor__email', 'editor__first_name', - 'editor__last_name',) - raw_id_fields = ('article', 'editor') - date_hierarchy = ('assigned') + list_display = ( + "pk", + "_article", + "_journal", + "editor", + "editor_type", + "assigned", + "notified", + ) + list_filter = ("article__journal", "editor_type", "assigned", "notified") + search_fields = ( + "article__pk", + "article__title", + "article__journal__code", + "editor__email", + "editor__first_name", + "editor__last_name", + ) + raw_id_fields = ("article", "editor") + date_hierarchy = "assigned" class ReviewRoundAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', 'round_number', 'date_started', - '_journal') - list_filter = ('article__journal', 'round_number', 'date_started') - search_fields = ('article__pk', 'article__title', 'article__journal__code') - raw_id_fields = ('article', 'review_files',) - date_hierarchy = ('date_started') + list_display = ("pk", "_article", "round_number", "date_started", "_journal") + list_filter = ("article__journal", "round_number", "date_started") + search_fields = ("article__pk", "article__title", "article__journal__code") + raw_id_fields = ( + "article", + "review_files", + ) + date_hierarchy = "date_started" class ReviewAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', 'reviewer', 'editor', - '_round', 'decision', 'date_requested', 'is_complete') - list_filter = ('article__journal', 'review_round__round_number', - 'decision', 'is_complete', - 'date_requested', 'date_accepted', - 'date_declined', 'date_complete', 'date_reminded', - 'date_due') - search_fields = ('article__pk', 'article__title', 'article__journal__code', - 'editor__email', 'editor__first_name', - 'editor__last_name', - 'reviewer__email', 'reviewer__first_name', - 'reviewer__last_name', - 'competing_interests', 'comments_for_editor') - raw_id_fields = ('article', 'reviewer', 'editor', 'review_round', - 'form', 'review_file') + list_display = ( + "pk", + "_article", + "_journal", + "reviewer", + "editor", + "_round", + "decision", + "date_requested", + "is_complete", + ) + list_filter = ( + "article__journal", + "review_round__round_number", + "decision", + "is_complete", + "date_requested", + "date_accepted", + "date_declined", + "date_complete", + "date_reminded", + "date_due", + ) + search_fields = ( + "article__pk", + "article__title", + "article__journal__code", + "editor__email", + "editor__first_name", + "editor__last_name", + "reviewer__email", + "reviewer__first_name", + "reviewer__last_name", + "competing_interests", + "comments_for_editor", + ) + raw_id_fields = ( + "article", + "reviewer", + "editor", + "review_round", + "form", + "review_file", + ) def _round(self, obj): - return obj.review_round.round_number if obj and obj.review_round else '' + return obj.review_round.round_number if obj and obj.review_round else "" inlines = [ admin_utils.ReviewAssignmentAnswerInline, @@ -55,134 +98,195 @@ def _round(self, obj): class ReviewFormAdmin(admin.ModelAdmin): - list_display = ('name', 'journal', 'deleted') - list_filter = ('journal', 'deleted') - filter_horizontal = ('elements',) - search_fields = ('name', 'journal__code', - 'intro', 'thanks') + list_display = ("name", "journal", "deleted") + list_filter = ("journal", "deleted") + filter_horizontal = ("elements",) + search_fields = ("name", "journal__code", "intro", "thanks") class ElementAdmin(admin.ModelAdmin): - list_display = ('name', 'kind', 'required', 'order', 'width') - list_filter = ('kind', 'required') - search_fields = ('name', 'help_text') + list_display = ("name", "kind", "required", "order", "width") + list_filter = ("kind", "required") + search_fields = ("name", "help_text") class FrozenReviewFormElementAdmin(admin.ModelAdmin): - list_display = ('name', 'kind', 'required', 'order', 'width') - list_filter = ('kind', 'required') - search_fields = ('name', 'help_text') - raw_id_fields = ('form_element', 'answer',) + list_display = ("name", "kind", "required", "order", "width") + list_filter = ("kind", "required") + search_fields = ("name", "help_text") + raw_id_fields = ( + "form_element", + "answer", + ) class AnswerAdmin(admin.ModelAdmin): - list_display = ('pk', '_element_answer', 'assignment', 'frozen_element', - 'author_can_see', '_journal') - list_display_links = ('_element_answer',) - list_filter = ('assignment__article__journal', 'assignment__date_complete', - 'assignment__date_due') - search_fields = ('assignment__article__pk', 'assignment__article__title', - 'assignment__reviewer__email', - 'assignment__reviewer__first_name', - 'assignment__reviewer__last_name', - 'answer', 'edited_answer') - raw_id_fields = ('assignment',) + list_display = ( + "pk", + "_element_answer", + "assignment", + "frozen_element", + "author_can_see", + "_journal", + ) + list_display_links = ("_element_answer",) + list_filter = ( + "assignment__article__journal", + "assignment__date_complete", + "assignment__date_due", + ) + search_fields = ( + "assignment__article__pk", + "assignment__article__title", + "assignment__reviewer__email", + "assignment__reviewer__first_name", + "assignment__reviewer__last_name", + "answer", + "edited_answer", + ) + raw_id_fields = ("assignment",) def _element_answer(self, obj): - return truncatewords_html(obj.answer, 5) if obj else '' + return truncatewords_html(obj.answer, 5) if obj else "" def _journal(self, obj): - return obj.assignment.article.journal if obj else '' + return obj.assignment.article.journal if obj else "" class RatingAdmin(admin.ModelAdmin): - list_display = ('pk', '_reviewer', 'rating', 'rater', '_journal', - '_assignment') - list_filter = ('assignment__article__journal', 'rating') - search_fields = ('assignment__article__pk', 'assignment__article__title', - 'assignment__reviewer__email', - 'assignment__reviewer__first_name', - 'assignment__reviewer__last_name', - 'rater__email', 'rater__first_name', - 'rater__last_name',) - raw_id_fields = ('assignment', 'rater') - date_hierarchy = ('assignment__date_complete') + list_display = ("pk", "_reviewer", "rating", "rater", "_journal", "_assignment") + list_filter = ("assignment__article__journal", "rating") + search_fields = ( + "assignment__article__pk", + "assignment__article__title", + "assignment__reviewer__email", + "assignment__reviewer__first_name", + "assignment__reviewer__last_name", + "rater__email", + "rater__first_name", + "rater__last_name", + ) + raw_id_fields = ("assignment", "rater") + date_hierarchy = "assignment__date_complete" def _reviewer(self, obj): return obj.assignment.reviewer def _journal(self, obj): - return obj.assignment.article.journal.code if obj else '' + return obj.assignment.article.journal.code if obj else "" def _assignment(self, obj): - return truncatewords(obj.assignment.__str__(), 10) if obj else '' + return truncatewords(obj.assignment.__str__(), 10) if obj else "" class RevisionActionAdmin(admin.ModelAdmin): - list_display = ('pk', 'logged', 'user', '_action_text') - raw_id_fields = ('user',) - search_fields = ('user__email', 'user__first_name', - 'user__last_name', 'text') - date_hierarchy = ('logged') + list_display = ("pk", "logged", "user", "_action_text") + raw_id_fields = ("user",) + search_fields = ("user__email", "user__first_name", "user__last_name", "text") + date_hierarchy = "logged" def _action_text(self, obj): - return truncatewords(obj.text, 5) if obj else '' + return truncatewords(obj.text, 5) if obj else "" class RevisionAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', 'editor', '_editor_note', - '_author', '_author_note', 'date_requested') - list_filter = ('article__journal', 'type', 'date_requested', - 'date_completed', 'date_due') - search_fields = ('article__pk', 'article__title', 'article__journal__code', - 'editor__email', 'editor__first_name', - 'editor__last_name', - 'article__correspondence_author__email', - 'article__correspondence_author__first_name', - 'article__correspondence_author__last_name', - 'editor_note', 'author_note') - raw_id_fields = ('article', 'editor') - filter_horizontal = ('actions',) + list_display = ( + "pk", + "_article", + "_journal", + "editor", + "_editor_note", + "_author", + "_author_note", + "date_requested", + ) + list_filter = ( + "article__journal", + "type", + "date_requested", + "date_completed", + "date_due", + ) + search_fields = ( + "article__pk", + "article__title", + "article__journal__code", + "editor__email", + "editor__first_name", + "editor__last_name", + "article__correspondence_author__email", + "article__correspondence_author__first_name", + "article__correspondence_author__last_name", + "editor_note", + "author_note", + ) + raw_id_fields = ("article", "editor") + filter_horizontal = ("actions",) inlines = [ admin_utils.RevisionActionInline, ] def _editor_note(self, obj): - return truncatewords_html(str(obj.editor_note), 5) if obj else '' + return truncatewords_html(str(obj.editor_note), 5) if obj else "" def _author_note(self, obj): - return truncatewords_html(str(obj.author_note), 5) if obj else '' + return truncatewords_html(str(obj.author_note), 5) if obj else "" class EditorOverrideAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', 'editor', 'overwritten') - list_filter = ('article__journal', 'overwritten') - search_fields = ('article__pk', 'article__title', 'article__journal__code', - 'editor__email', 'editor__first_name', - 'editor__last_name', - 'article__correspondence_author__email', - 'article__correspondence_author__first_name', - 'article__correspondence_author__last_name') - raw_id_fields = ('article', 'editor') + list_display = ("pk", "_article", "_journal", "editor", "overwritten") + list_filter = ("article__journal", "overwritten") + search_fields = ( + "article__pk", + "article__title", + "article__journal__code", + "editor__email", + "editor__first_name", + "editor__last_name", + "article__correspondence_author__email", + "article__correspondence_author__first_name", + "article__correspondence_author__last_name", + ) + raw_id_fields = ("article", "editor") class DraftAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', '_article', '_journal', 'section_editor', 'decision', - 'drafted', 'editor', 'editor_decision') - list_filter = ('article__journal', 'decision', 'editor_decision', - 'drafted', 'revision_request_due_date') - search_fields = ('article__pk', 'article__title', 'article__journal__code', - 'editor__email', 'editor__first_name', - 'editor__last_name', - 'section_editor__email', 'section_editor__first_name', - 'section_editor__last_name', - 'article__correspondence_author__email', - 'article__correspondence_author__first_name', - 'article__correspondence_author__last_name', - 'message_to_editor', 'email_message', - 'editor_decline_rationale') - raw_id_fields = ('article', 'editor', 'section_editor') + list_display = ( + "pk", + "_article", + "_journal", + "section_editor", + "decision", + "drafted", + "editor", + "editor_decision", + ) + list_filter = ( + "article__journal", + "decision", + "editor_decision", + "drafted", + "revision_request_due_date", + ) + search_fields = ( + "article__pk", + "article__title", + "article__journal__code", + "editor__email", + "editor__first_name", + "editor__last_name", + "section_editor__email", + "section_editor__first_name", + "section_editor__last_name", + "article__correspondence_author__email", + "article__correspondence_author__first_name", + "article__correspondence_author__last_name", + "message_to_editor", + "email_message", + "editor_decline_rationale", + ) + raw_id_fields = ("article", "editor", "section_editor") admin_list = [ diff --git a/src/review/apps.py b/src/review/apps.py index 9f3205cab4..79619d8212 100755 --- a/src/review/apps.py +++ b/src/review/apps.py @@ -7,4 +7,4 @@ class ReviewConfig(AppConfig): - name = 'review' + name = "review" diff --git a/src/review/const.py b/src/review/const.py index 380bba94a8..d2dd74ac32 100644 --- a/src/review/const.py +++ b/src/review/const.py @@ -1,28 +1,29 @@ """ Constants defined by the review app """ + from utils.const import EnumContains class EditorialDecisions(EnumContains): - ACCEPT = 'accept' - DECLINE = 'decline' - UNDECLINE = 'undecline' - MINOR_REVISIONS = 'minor_revisions' - MAJOR_REVISIONS = 'major_revisions' - REVIEW = 'review' + ACCEPT = "accept" + DECLINE = "decline" + UNDECLINE = "undecline" + MINOR_REVISIONS = "minor_revisions" + MAJOR_REVISIONS = "major_revisions" + REVIEW = "review" class ReviewerDecisions(EnumContains): - DECISION_ACCEPT = 'accept' - DECISION_MINOR = 'minor_revisions' - DECISION_MAJOR = 'major_revisions' - DECISION_REJECT = 'reject' - DECISION_NO_RECOMMENDATION = 'none' - DECISION_WITHDRAWN = 'withdrawn' + DECISION_ACCEPT = "accept" + DECISION_MINOR = "minor_revisions" + DECISION_MAJOR = "major_revisions" + DECISION_REJECT = "reject" + DECISION_NO_RECOMMENDATION = "none" + DECISION_WITHDRAWN = "withdrawn" class VisibilityOptions(EnumContains): - OPEN = 'open' - SINGLE_ANON = 'blind' - DOUBLE_ANON = 'double-blind' + OPEN = "open" + SINGLE_ANON = "blind" + DOUBLE_ANON = "double-blind" diff --git a/src/review/forms.py b/src/review/forms.py index 344886edda..19f44847f5 100755 --- a/src/review/forms.py +++ b/src/review/forms.py @@ -20,91 +20,93 @@ class DraftDecisionForm(forms.ModelForm): - widgets = {'revision_request_due_date': HTMLDateInput()} + widgets = {"revision_request_due_date": HTMLDateInput()} class Meta: model = models.DecisionDraft - exclude = ( - 'section_editor', 'article', 'editor_decision' - ) + exclude = ("section_editor", "article", "editor_decision") def __init__(self, *args, **kwargs): - newly_created = kwargs.get('instance') is None - message_to_editor = kwargs.pop('message_to_editor', None) - editors = kwargs.pop('editors', []) + newly_created = kwargs.get("instance") is None + message_to_editor = kwargs.pop("message_to_editor", None) + editors = kwargs.pop("editors", []) super(DraftDecisionForm, self).__init__(*args, **kwargs) - self.fields['message_to_editor'].initial = linebreaksbr(message_to_editor) - self.fields['revision_request_due_date'].widget = HTMLDateInput() - self.fields['revision_request_due_date'].widget.attrs['onchange'] = 'decision_change()' - self.fields['decision'].widget.attrs[ - 'onchange'] = 'decision_change()' - self.fields['decision'].widget.attrs[ - 'onfocus'] = 'store_previous_decision()' - self.fields['editor'].queryset = editors + self.fields["message_to_editor"].initial = linebreaksbr(message_to_editor) + self.fields["revision_request_due_date"].widget = HTMLDateInput() + self.fields["revision_request_due_date"].widget.attrs["onchange"] = ( + "decision_change()" + ) + self.fields["decision"].widget.attrs["onchange"] = "decision_change()" + self.fields["decision"].widget.attrs["onfocus"] = "store_previous_decision()" + self.fields["editor"].queryset = editors if not newly_created: - self.fields['message_to_editor'].widget = forms.HiddenInput() - self.fields['editor'].widget = forms.HiddenInput() + self.fields["message_to_editor"].widget = forms.HiddenInput() + self.fields["editor"].widget = forms.HiddenInput() class ReviewAssignmentForm(forms.ModelForm, core_forms.ConfirmableIfErrorsForm): class Meta: model = models.ReviewAssignment - fields = ('visibility', 'form', 'date_due', 'reviewer') + fields = ("visibility", "form", "date_due", "reviewer") widgets = { - 'date_due': HTMLDateInput, + "date_due": HTMLDateInput, } def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) - self.article = kwargs.pop('article') - self.editor = kwargs.pop('editor') - self.reviewers = kwargs.pop('reviewers') + self.journal = kwargs.pop("journal", None) + self.article = kwargs.pop("article") + self.editor = kwargs.pop("editor") + self.reviewers = kwargs.pop("reviewers") super(ReviewAssignmentForm, self).__init__(*args, **kwargs) - self.fields['form'].empty_label = None + self.fields["form"].empty_label = None default_visibility = setting_handler.get_setting( - 'general', - 'default_review_visibility', + "general", + "default_review_visibility", self.journal, create=True, ) default_due = setting_handler.get_setting( - 'general', - 'default_review_days', + "general", + "default_review_days", self.journal, create=True, ).value default_form = setting_handler.get_setting( - 'general', - 'default_review_form', + "general", + "default_review_form", self.journal, create=True, ).processed_value if self.journal: - self.fields['form'].queryset = models.ReviewForm.objects.filter(journal=self.journal, deleted=False) + self.fields["form"].queryset = models.ReviewForm.objects.filter( + journal=self.journal, deleted=False + ) if default_visibility.value: - self.fields['visibility'].initial = default_visibility.value + self.fields["visibility"].initial = default_visibility.value if default_due: due_date = timezone.now() + timedelta(days=int(default_due)) - self.fields['date_due'].initial = due_date + self.fields["date_due"].initial = due_date if default_form and not self.instance.form: try: - self.fields['form'].initial = models.ReviewForm.objects.get(pk=default_form) + self.fields["form"].initial = models.ReviewForm.objects.get( + pk=default_form + ) except models.ReviewForm.DoesNotExist: - self.fields['form'].initial = '' + self.fields["form"].initial = "" if self.reviewers: - self.fields['reviewer'].queryset = self.reviewers + self.fields["reviewer"].queryset = self.reviewers if self.instance.date_accepted: # Form should not be changed after request has been accepted - self.fields['form'].initial = self.instance.form - self.fields['form'].disabled = True + self.fields["form"].initial = self.instance.form + self.fields["form"].disabled = True def save(self, commit=True): review_assignment = super().save(commit=False) @@ -122,9 +124,11 @@ def check_for_potential_errors(self): # This customizes the confirmable form method potential_errors = [] - one_click_access = setting_handler.get_setting('general', 'enable_one_click_access', self.journal).value + one_click_access = setting_handler.get_setting( + "general", "enable_one_click_access", self.journal + ).value if not one_click_access: - reviewer = self.cleaned_data.get('reviewer', None) + reviewer = self.cleaned_data.get("reviewer", None) message = self.check_for_inactive_account(reviewer) if message: potential_errors.append(message) @@ -135,63 +139,66 @@ def check_for_potential_errors(self): class BulkReviewAssignmentForm(forms.ModelForm): template = forms.CharField( widget=TinyMCE, - label='Email Template', + label="Email Template", ) reviewer_csv = forms.FileField( - label='Upload Reviewer CSV File', + label="Upload Reviewer CSV File", ) def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(BulkReviewAssignmentForm, self).__init__(*args, **kwargs) - self.fields['form'].queryset = models.ReviewForm.objects.filter( + self.fields["form"].queryset = models.ReviewForm.objects.filter( journal=self.journal, deleted=False, ) review_assignment_template = setting_handler.get_setting( - setting_group_name='email', - setting_name='review_assignment', + setting_group_name="email", + setting_name="review_assignment", journal=self.journal, ).processed_value - self.fields['template'].initial = review_assignment_template + self.fields["template"].initial = review_assignment_template class Meta: model = models.ReviewAssignment - fields = ('visibility', 'form', 'date_due') + fields = ("visibility", "form", "date_due") widgets = { - 'date_due': HTMLDateInput, + "date_due": HTMLDateInput, } class EditReviewAssignmentForm(forms.ModelForm): class Meta: model = models.ReviewAssignment - fields = ('visibility', 'form', 'date_due') + fields = ("visibility", "form", "date_due") widgets = { - 'date_due': HTMLDateInput, + "date_due": HTMLDateInput, } def __init__(self, *args, **kwargs): - self.journal = kwargs.pop('journal', None) + self.journal = kwargs.pop("journal", None) super(EditReviewAssignmentForm, self).__init__(*args, **kwargs) if self.journal: - self.fields['form'].queryset = models.ReviewForm.objects.filter(journal=self.journal, deleted=False) + self.fields["form"].queryset = models.ReviewForm.objects.filter( + journal=self.journal, deleted=False + ) class ReviewerDecisionForm(forms.ModelForm): - def __init__(self, *args, **kwargs): decision_required = kwargs.pop("decision_required", False) open_review_initial = kwargs.pop("open_review_initial", False) self.recommendation_disabled = kwargs.pop("recommendation_disabled", False) super().__init__(*args, **kwargs) - self.fields['decision'].required = decision_required - self.fields['decision'].choices = models.reviewer_decision_choices() - self.fields['permission_to_make_public'].widget.attrs['checked'] = open_review_initial + self.fields["decision"].required = decision_required + self.fields["decision"].choices = models.reviewer_decision_choices() + self.fields["permission_to_make_public"].widget.attrs["checked"] = ( + open_review_initial + ) if self.instance: if self.recommendation_disabled: - del self.fields['decision'] + del self.fields["decision"] def save(self, commit=True): review_assignment = super().save(commit=False) @@ -207,52 +214,51 @@ def save(self, commit=True): class Meta: model = models.ReviewAssignment - fields = ('decision', 'comments_for_editor', 'permission_to_make_public') + fields = ("decision", "comments_for_editor", "permission_to_make_public") class FakeReviewerDecisionForm(FakeModelForm, ReviewerDecisionForm): - def __init__(self, *args, **kwargs): kwargs["disable_fields"] = True - open_peer_review = kwargs.pop('open_peer_review', None) + open_peer_review = kwargs.pop("open_peer_review", None) super().__init__(*args, **kwargs) if not (open_peer_review and open_peer_review.value): - del self.fields['permission_to_make_public'] + del self.fields["permission_to_make_public"] class ReplacementFileDetails(forms.ModelForm): class Meta: model = core_models.File fields = ( - 'label', - 'description', + "label", + "description", ) class SuggestReviewers(forms.ModelForm): class Meta: model = models.ReviewAssignment - fields = ( - 'suggested_reviewers', - ) + fields = ("suggested_reviewers",) class RevisionRequest(forms.ModelForm, core_forms.ConfirmableIfErrorsForm): - QUESTION = _('Are you sure you want to request revisions?') + QUESTION = _("Are you sure you want to request revisions?") class Meta: model = models.RevisionRequest fields = ( - 'date_due', 'type', 'editor_note', + "date_due", + "type", + "editor_note", ) widgets = { - 'date_due': HTMLDateInput, + "date_due": HTMLDateInput, } def __init__(self, *args, **kwargs): - self.editor = kwargs.pop('editor', None) - self.article = kwargs.pop('article', None) + self.editor = kwargs.pop("editor", None) + self.article = kwargs.pop("article", None) super().__init__(*args, **kwargs) def save(self, commit=True): @@ -278,46 +284,43 @@ def check_for_potential_errors(self): class EditRevisionDue(forms.ModelForm): class Meta: model = models.RevisionRequest - fields = ( - 'date_due', - ) + fields = ("date_due",) widgets = { - 'date_due': HTMLDateInput, + "date_due": HTMLDateInput, } class DoRevisions(forms.ModelForm, core_forms.ConfirmableForm): - # Confirmable form constants - QUESTION = _('Are you sure you want to complete the revision request?') + QUESTION = _("Are you sure you want to complete the revision request?") class Meta: model = models.RevisionRequest fields = ( - 'author_note', - 'response_letter', + "author_note", + "response_letter", ) def check_for_potential_errors(self): # This customizes the confirmable form method potential_errors = [] - if not self.cleaned_data.get('author_note', None): - message = 'The Covering Letter field is empty.' + if not self.cleaned_data.get("author_note", None): + message = "The Covering Letter field is empty." potential_errors.append(_(message)) - if not self.cleaned_data.get('response_letter', None): - message = 'The Response Letter field is empty.' + if not self.cleaned_data.get("response_letter", None): + message = "The Response Letter field is empty." potential_errors.append(_(message)) ms_files = self.instance.article.manuscript_files.all() if ms_files: last_upload = max(set(ms_file.date_uploaded for ms_file in ms_files)) if self.instance.date_requested > last_upload: - message = 'No manuscript files have been replaced or added.' + message = "No manuscript files have been replaced or added." potential_errors.append(_(message)) else: - message = 'No manuscript files have been uploaded.' + message = "No manuscript files have been uploaded." potential_errors.append(_(message)) return potential_errors @@ -325,11 +328,11 @@ def check_for_potential_errors(self): class GeneratedForm(forms.Form): def __init__(self, *args, **kwargs): - review_assignment = kwargs.pop('review_assignment', None) - fields_required = kwargs.pop('fields_required', True) - answer = kwargs.pop('answer', None) - preview = kwargs.pop('preview', None) - if 'initial' not in kwargs and review_assignment: + review_assignment = kwargs.pop("review_assignment", None) + fields_required = kwargs.pop("fields_required", True) + answer = kwargs.pop("answer", None) + preview = kwargs.pop("preview", None) + if "initial" not in kwargs and review_assignment: kwargs["initial"] = { a.original_element.name: a.answer for a in review_assignment.review_form_answers() @@ -345,40 +348,52 @@ def __init__(self, *args, **kwargs): elements = review_assignment.form.elements.all() for element in elements: - if element.kind == 'text': + if element.kind == "text": self.fields[str(element.pk)] = forms.CharField( - widget=forms.TextInput(attrs={'div_class': element.width}), - required=element.required if fields_required else False) - elif element.kind == 'textarea': + widget=forms.TextInput(attrs={"div_class": element.width}), + required=element.required if fields_required else False, + ) + elif element.kind == "textarea": self.fields[str(element.pk)] = forms.CharField( widget=TinyMCE, - required=element.required if fields_required else False + required=element.required if fields_required else False, ) - elif element.kind == 'date': + elif element.kind == "date": self.fields[str(element.pk)] = forms.CharField( - widget=forms.DateInput(attrs={'class': 'datepicker', 'div_class': element.width}), - required=element.required if fields_required else False) - elif element.kind == 'upload': - self.fields[str(element.pk)] = forms.FileField(required=element.required if fields_required else False) - elif element.kind == 'select': + widget=forms.DateInput( + attrs={"class": "datepicker", "div_class": element.width} + ), + required=element.required if fields_required else False, + ) + elif element.kind == "upload": + self.fields[str(element.pk)] = forms.FileField( + required=element.required if fields_required else False + ) + elif element.kind == "select": choices = logic.render_choices(element.choices) self.fields[str(element.pk)] = forms.ChoiceField( - widget=forms.Select(attrs={'div_class': element.width}), choices=choices, - required=element.required if fields_required else False) - elif element.kind == 'email': + widget=forms.Select(attrs={"div_class": element.width}), + choices=choices, + required=element.required if fields_required else False, + ) + elif element.kind == "email": self.fields[str(element.pk)] = forms.EmailField( - widget=forms.TextInput(attrs={'div_class': element.width}), - required=element.required if fields_required else False) - elif element.kind == 'check': + widget=forms.TextInput(attrs={"div_class": element.width}), + required=element.required if fields_required else False, + ) + elif element.kind == "check": self.fields[str(element.pk)] = forms.BooleanField( - widget=forms.CheckboxInput(attrs={'is_checkbox': True}), - required=element.required if fields_required else False) + widget=forms.CheckboxInput(attrs={"is_checkbox": True}), + required=element.required if fields_required else False, + ) self.fields[str(element.pk)].help_text = element.help_text self.fields[str(element.pk)].label = element.name if answer: - self.fields[str(element.pk)].initial = answer.edited_answer if answer.edited_answer else answer.answer + self.fields[str(element.pk)].initial = ( + answer.edited_answer if answer.edited_answer else answer.answer + ) if review_assignment: answers = review_assignment.review_form_answers() @@ -386,20 +401,23 @@ def __init__(self, *args, **kwargs): try: answer = answers.get(original_element=element) self.fields[str(element.pk)].initial = answer.answer - except (models.ReviewFormAnswer.DoesNotExist, models.ReviewAssignmentAnswer.DoesNotExist): + except ( + models.ReviewFormAnswer.DoesNotExist, + models.ReviewAssignmentAnswer.DoesNotExist, + ): pass class NewForm(forms.ModelForm): class Meta: model = models.ReviewForm - exclude = ('journal', 'elements', 'deleted') + exclude = ("journal", "elements", "deleted") class ElementForm(forms.ModelForm): class Meta: model = models.ReviewFormElement - exclude = ('',) + exclude = ("",) class ReviewReminderForm(forms.Form): @@ -410,7 +428,7 @@ class ReviewReminderForm(forms.Form): class ReviewVisibilityForm(forms.ModelForm): class Meta: model = models.ReviewAssignment - fields = ('for_author_consumption', 'display_review_file') + fields = ("for_author_consumption", "display_review_file") labels = { "for_author_consumption": _("Author can access this review"), "display_review_file": _("Author can access review file"), @@ -423,17 +441,16 @@ class Meta: def __init__(self, *args, **kwargs): super(ReviewVisibilityForm, self).__init__(*args, **kwargs) if not self.instance.review_file: - self.fields.pop('display_review_file') + self.fields.pop("display_review_file") class AnswerVisibilityForm(forms.Form): - def __init__(self, *args, **kwargs): - self.review_assignment = kwargs.pop('review_assignment', None) + self.review_assignment = kwargs.pop("review_assignment", None) super(AnswerVisibilityForm, self).__init__(*args, **kwargs) for answer in self.review_assignment.review_form_answers(): - label = _("Author can see ‘%(name)s’") % {'name': answer.best_label} + label = _("Author can see ‘%(name)s’") % {"name": answer.best_label} self.fields[str(answer.pk)] = forms.BooleanField( label=label, widget=HTMLSwitchInput(), @@ -455,12 +472,12 @@ def save(self, commit=True): class ShareReviewsForm(forms.Form): def __init__(self, *args, **kwargs): - reviews = kwargs.pop('reviews', None) + reviews = kwargs.pop("reviews", None) super(ShareReviewsForm, self).__init__(*args, **kwargs) for review in reviews: - self.fields[f'{ review.pk }'] = forms.CharField( + self.fields[f"{ review.pk }"] = forms.CharField( widget=TinyMCE, - label=f'Email for {review.reviewer.full_name()}', + label=f"Email for {review.reviewer.full_name()}", initial=review.email_content, ) diff --git a/src/review/hypothesis.py b/src/review/hypothesis.py index 1f923236d8..7e4a8f6bbc 100644 --- a/src/review/hypothesis.py +++ b/src/review/hypothesis.py @@ -8,21 +8,28 @@ def create_hypothesis_account(account): - payload = { - 'authority': settings.HYPOTHESIS_CLIENT_AUTHORITY, - 'username': account.hypothesis_username, - 'email': account.email, - 'display_name': account.full_name() + "authority": settings.HYPOTHESIS_CLIENT_AUTHORITY, + "username": account.hypothesis_username, + "email": account.email, + "display_name": account.full_name(), } - url = '{base_url}users'.format(base_url=settings.HYPOTHESIS_BASE_URL) + url = "{base_url}users".format(base_url=settings.HYPOTHESIS_BASE_URL) - r = requests.post(url, - auth=HTTPBasicAuth(settings.HYPOTHESIS_CLIENT_ID, settings.HYPOTHESIS_CLIENT_SECRET), - json=payload) + r = requests.post( + url, + auth=HTTPBasicAuth( + settings.HYPOTHESIS_CLIENT_ID, settings.HYPOTHESIS_CLIENT_SECRET + ), + json=payload, + ) if not r.status_code == 200 and settings.DEBUG: - print('There was an error generating the hypothes.is account for this user. Code {0}.'.format(r.status_code)) + print( + "There was an error generating the hypothes.is account for this user. Code {0}.".format( + r.status_code + ) + ) print(payload) print(r.text) print(r.headers) @@ -35,14 +42,18 @@ def generate_grant_token(account): :return: jwt encoded token """ now = datetime.utcnow() - userid = 'acct:{username}@{authority}'.format(username=account.hypothesis_username, - authority=settings.HYPOTHESIS_CLIENT_AUTHORITY) + userid = "acct:{username}@{authority}".format( + username=account.hypothesis_username, + authority=settings.HYPOTHESIS_CLIENT_AUTHORITY, + ) payload = { - 'aud': 'hypothes.is', - 'iss': settings.HYPOTHESIS_CLIENT_AUTHORITY_ID, - 'sub': userid, - 'nbf': now, - 'exp': now + timedelta(minutes=10), + "aud": "hypothes.is", + "iss": settings.HYPOTHESIS_CLIENT_AUTHORITY_ID, + "sub": userid, + "nbf": now, + "exp": now + timedelta(minutes=10), } - return jwt.encode(payload, settings.HYPOTHESIS_CLIENT_AUTHORITY_SECRET, algorithm='HS256') + return jwt.encode( + payload, settings.HYPOTHESIS_CLIENT_AUTHORITY_SECRET, algorithm="HS256" + ) diff --git a/src/review/logic.py b/src/review/logic.py index b9c1e32c56..418836972e 100755 --- a/src/review/logic.py +++ b/src/review/logic.py @@ -30,7 +30,7 @@ from docx import Document from utils import render_template, setting_handler, notify_helpers -from core import( +from core import ( files, email, models as core_models, @@ -43,62 +43,79 @@ def get_reviewers(article, candidate_queryset, exclude_pks): prefetch_review_assignment = Prefetch( - 'reviewer', + "reviewer", queryset=models.ReviewAssignment.objects.filter( article__journal=article.journal - ).exclude(date_complete__isnull=True).order_by("-date_complete") + ) + .exclude(date_complete__isnull=True) + .order_by("-date_complete"), + ) + active_reviews_count = ( + models.ReviewAssignment.objects.filter( + is_complete=False, + reviewer=OuterRef("id"), + ) + .values( + "reviewer_id", + ) + .annotate( + rev_count=Count("reviewer_id"), + ) + .values("rev_count") + ) + + rating_average = ( + models.ReviewerRating.objects.filter( + assignment__article__journal=article.journal, + assignment__reviewer=OuterRef("id"), + ) + .values( + # Without this .values call, results are grouped by assignment... + "assignment__article__journal", + ) + .annotate( + rating_average=Avg("rating"), + ) + .values("rating_average") ) - active_reviews_count = models.ReviewAssignment.objects.filter( - is_complete=False, - reviewer=OuterRef("id"), - ).values( - "reviewer_id", - ).annotate( - rev_count=Count("reviewer_id"), - ).values("rev_count") - - rating_average = models.ReviewerRating.objects.filter( - assignment__article__journal=article.journal, - assignment__reviewer=OuterRef("id"), - ).values( - # Without this .values call, results are grouped by assignment... - "assignment__article__journal", - ).annotate( - rating_average=Avg("rating"), - ).values("rating_average") - - completed_reviewer_pks_subquery = article.completed_reviews_with_decision.values_list( - 'reviewer__pk', - flat=True, + + completed_reviewer_pks_subquery = ( + article.completed_reviews_with_decision.values_list( + "reviewer__pk", + flat=True, + ) ) # TODO swap the below subqueries with filtered annotations on Django 2.0+ - reviewers = candidate_queryset.exclude( - pk__in=exclude_pks, - ).prefetch_related( - prefetch_review_assignment, - 'interest', - ).annotate( - active_reviews_count=Subquery( - active_reviews_count, - output_field=IntegerField(), - ) - ).annotate( - rating_average=Subquery(rating_average, output_field=IntegerField()), - is_past_reviewer=Case( - When(pk__in=Subquery(completed_reviewer_pks_subquery), - then=True), - default=False, - output_field=BooleanField(), - ), + reviewers = ( + candidate_queryset.exclude( + pk__in=exclude_pks, + ) + .prefetch_related( + prefetch_review_assignment, + "interest", + ) + .annotate( + active_reviews_count=Subquery( + active_reviews_count, + output_field=IntegerField(), + ) + ) + .annotate( + rating_average=Subquery(rating_average, output_field=IntegerField()), + is_past_reviewer=Case( + When(pk__in=Subquery(completed_reviewer_pks_subquery), then=True), + default=False, + output_field=BooleanField(), + ), + ) ) - if article.journal.get_setting('general', 'enable_suggested_reviewers'): + if article.journal.get_setting("general", "enable_suggested_reviewers"): article_keywords = [keyword.word for keyword in article.keywords.all()] reviewers = reviewers.annotate( is_suggested_reviewer=Case( - When(interest__name__in=article_keywords, - then=Value(True)), + When(interest__name__in=article_keywords, then=Value(True)), default=Value(False), output_field=BooleanField(), ) @@ -108,13 +125,14 @@ def get_reviewers(article, candidate_queryset, exclude_pks): def get_reviewer_candidates(article, user=None, reviewers_to_exclude=None): - """ Builds a queryset of candidates for peer review for the given article + """Builds a queryset of candidates for peer review for the given article :param article: an instance of submission.models.Article :param user: The user requesting candidates who would be filtered out :param reviewers_to_exclude: queryset of Account objects """ reviewer_pks_to_exclude = [ - review.reviewer.pk for review in article.reviewassignment_set.filter( + review.reviewer.pk + for review in article.reviewassignment_set.filter( review_round=article.current_review_round_object(), ) ] @@ -128,9 +146,7 @@ def get_reviewer_candidates(article, user=None, reviewers_to_exclude=None): ) return get_reviewers( - article, - article.journal.users_with_role('reviewer'), - reviewer_pks_to_exclude + article, article.journal.users_with_role("reviewer"), reviewer_pks_to_exclude ) @@ -154,10 +170,9 @@ def get_previous_round_reviewers(article): :param article: an instance of submission.models.Article """ review_assignments = article.reviewassignment_set.filter( - review_round=article.current_review_round_object()) - reviewer_pks_to_exclude = [ - review.reviewer.pk for review in review_assignments - ] + review_round=article.current_review_round_object() + ) + reviewer_pks_to_exclude = [review.reviewer.pk for review in review_assignments] completed_review_reviewer_pks = [ review.reviewer.pk for review in article.completed_reviews_with_decision @@ -173,30 +188,26 @@ def get_previous_round_reviewers(article): def get_assignment_context(request, article, editor, assignment): review_in_review_url = request.journal.site_url( - reverse( - 'review_in_review', - kwargs={'article_id': article.pk} - ) + reverse("review_in_review", kwargs={"article_id": article.pk}) ) email_context = { - 'article': article, - 'editor': editor, - 'assignment': assignment, - 'review_in_review_url': review_in_review_url, + "article": article, + "editor": editor, + "assignment": assignment, + "review_in_review_url": review_in_review_url, } return email_context - def get_review_url(request, review_assignment): - review_url = request.journal.site_url(path=reverse( - 'do_review', kwargs={'assignment_id': review_assignment.id} - )) + review_url = request.journal.site_url( + path=reverse("do_review", kwargs={"assignment_id": review_assignment.id}) + ) access_codes = setting_handler.get_setting( - 'general', - 'enable_one_click_access', + "general", + "enable_one_click_access", request.journal, ).value @@ -226,66 +237,66 @@ def get_article_details_for_review(article): def get_reviewer_notification_context( - request, article, editor, + request, + article, + editor, review_assignment, ): review_url = get_review_url(request, review_assignment) article_details = get_article_details_for_review(article) email_context = { - 'article': article, - 'editor': editor, - 'review_assignment': review_assignment, - 'review_url': review_url, - 'article_details': article_details, + "article": article, + "editor": editor, + "review_assignment": review_assignment, + "review_url": review_url, + "article_details": article_details, } return email_context def get_reviewer_notification( - request, article, editor, review_assignment, + request, + article, + editor, + review_assignment, reminder=False, ): - email_context = get_reviewer_notification_context( - request, article, editor, review_assignment, + request, + article, + editor, + review_assignment, ) - if reminder and reminder == 'request': + if reminder and reminder == "request": return render_template.get_message_content( - request, - email_context, - 'default_review_reminder' + request, email_context, "default_review_reminder" ) - elif reminder and reminder == 'accepted': + elif reminder and reminder == "accepted": return render_template.get_message_content( - request, - email_context, - 'accepted_review_reminder' + request, email_context, "accepted_review_reminder" ) else: return render_template.get_message_content( - request, - email_context, - 'review_assignment' + request, email_context, "review_assignment" ) def get_withdrawal_notification_context(request, review_assignment): - email_context = { - 'article': review_assignment.article, - 'review_assignment': review_assignment, - 'editor': request.user, + "article": review_assignment.article, + "review_assignment": review_assignment, + "editor": request.user, } return email_context def get_unassignment_context(request, assignment): email_context = { - 'article': assignment.article, - 'assignment': assignment, - 'editor': request.user, + "article": assignment.article, + "assignment": assignment, + "editor": request.user, } return email_context @@ -293,9 +304,9 @@ def get_unassignment_context(request, assignment): def get_decision_context(request, article, decision, author_review_url): email_context = { - 'article': article, - 'decision': decision, - 'review_url': author_review_url, + "article": article, + "decision": decision, + "review_url": author_review_url, } return email_context @@ -314,42 +325,43 @@ def get_decision_content(request, article, decision, author_review_url): def get_revision_request_content(request, article, revision, draft=False): email_context = { - 'article': article, - 'revision': revision, + "article": article, + "revision": revision, } if not draft: - do_revisions_url = request.journal.site_url(path=reverse( - 'do_revisions', - kwargs={ - 'article_id': article.pk, - 'revision_id': revision.pk, - } - )) - email_context['do_revisions_url'] = do_revisions_url + do_revisions_url = request.journal.site_url( + path=reverse( + "do_revisions", + kwargs={ + "article_id": article.pk, + "revision_id": revision.pk, + }, + ) + ) + email_context["do_revisions_url"] = do_revisions_url else: - email_context['do_revisions_url'] = "{{ do_revisions_url }}" + email_context["do_revisions_url"] = "{{ do_revisions_url }}" - return render_template.get_message_content(request, email_context, 'request_revisions') + return render_template.get_message_content( + request, email_context, "request_revisions" + ) def get_share_review_content(request, article, review): url = request.journal.site_url( - reverse( - 'reviewer_share_reviews', - kwargs={'article_id': article.pk} - ) + reverse("reviewer_share_reviews", kwargs={"article_id": article.pk}) ) email_context = { - 'article': article, - 'review': review, - 'url': url, + "article": article, + "review": review, + "url": url, } return render_template.get_message_content( request, email_context, - 'share_reviews_notification', + "share_reviews_notification", ) @@ -365,17 +377,17 @@ def send_review_share_message(request, article, subject, form_data): review.reviewer.email, email_content, log_dict={ - 'level': 'Info', - 'action_text': f'Reviews link shared with {review.reviewer.full_name()}', - 'types': 'Review Sharing', - 'actor': request.user, - 'target': article, - } + "level": "Info", + "action_text": f"Reviews link shared with {review.reviewer.full_name()}", + "types": "Review Sharing", + "actor": request.user, + "target": article, + }, ) def get_reviewer_from_post(request): - reviewer_id = request.POST.get('reviewer') + reviewer_id = request.POST.get("reviewer") if reviewer_id: reviewer = core_models.Account.objects.get(pk=reviewer_id) @@ -391,9 +403,7 @@ def get_reviewer_from_post(request): def log_revision_event(text, user, revision_request): action = models.RevisionAction.objects.create( - text=text, - logged=timezone.now(), - user=user + text=text, logged=timezone.now(), user=user ) revision_request.actions.add(action) @@ -401,16 +411,14 @@ def log_revision_event(text, user, revision_request): def get_draft_email_message(request, article): review_in_review_url = request.journal.site_url( - path=reverse( - 'review_draft_decision', args=[article.pk] - ) + path=reverse("review_draft_decision", args=[article.pk]) ) email_context = { - 'article': article, - 'review_in_review_url': review_in_review_url, + "article": article, + "review_in_review_url": review_in_review_url, } - return render_template.get_message_content(request, email_context, 'draft_message') + return render_template.get_message_content(request, email_context, "draft_message") def group_files(article, reviews): @@ -434,15 +442,14 @@ def handle_draft_declined(article, draft_decision, request): Raises an event when a draft decision is declined by an editor. """ kwargs = { - 'article': article, - 'request': request, - 'draft_decision': draft_decision, - 'skip': False, + "article": article, + "request": request, + "draft_decision": draft_decision, + "skip": False, } draft_decision.editor_decline_rationale = request.POST.get( - 'editor_decline_rationale', - '

Editor provided no rationale.

' + "editor_decline_rationale", "

Editor provided no rationale.

" ) draft_decision.save() @@ -455,15 +462,15 @@ def handle_draft_declined(article, draft_decision, request): def handle_decision_action(article, draft, request): # Setup email data - subject = 'Decision' + subject = "Decision" subject_setting_name = None - user_message = request.POST.get('email_message', 'No message found.') + user_message = request.POST.get("email_message", "No message found.") if draft.decision == ED.ACCEPT.value: - subject_setting_name = 'subject_review_decision_accept' + subject_setting_name = "subject_review_decision_accept" elif draft.decision == ED.DECLINE.value: - subject_setting_name = 'subject_review_decision_decline' + subject_setting_name = "subject_review_decision_decline" elif draft.decision == ED.UNDECLINE.value: - subject_setting_name = 'subject_review_decision_undecline' + subject_setting_name = "subject_review_decision_undecline" if subject_setting_name: subject = setting_handler.get_email_subject_setting( setting_name=subject_setting_name, @@ -475,11 +482,11 @@ def handle_decision_action(article, draft, request): ) kwargs = { - 'article': article, - 'request': request, - 'decision': draft.decision, - 'skip': False, - 'email_data': email_data, + "article": article, + "request": request, + "decision": draft.decision, + "skip": False, + "email_data": email_data, } if draft.decision == ED.ACCEPT.value: @@ -491,10 +498,10 @@ def handle_decision_action(article, draft, request): ) # Call workflow element complete event workflow_kwargs = { - 'handshake_url': 'review_home', - 'request': request, - 'article': article, - 'switch_stage': True, + "handshake_url": "review_home", + "request": request, + "article": article, + "switch_stage": True, } return event_logic.Events.raise_event( event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, @@ -508,43 +515,45 @@ def handle_decision_action(article, draft, request): task_object=article, **kwargs, ) - elif draft.decision == 'minor_revisions' or draft.decision == 'major_revisions': + elif draft.decision == "minor_revisions" or draft.decision == "major_revisions": revision = models.RevisionRequest.objects.create( article=article, editor=draft.section_editor, - editor_note='', + editor_note="", type=draft.decision, - date_due=draft.revision_request_due_date + date_due=draft.revision_request_due_date, + ) + do_revisions_url = request.journal.site_url( + path=reverse( + "do_revisions", + kwargs={ + "article_id": article.pk, + "revision_id": revision.pk, + }, + ) ) - do_revisions_url = request.journal.site_url(path=reverse( - 'do_revisions', - kwargs={ - 'article_id': article.pk, - 'revision_id': revision.pk, - } - )) revision_rendered_template = render_template.get_message_content( request, - {'do_revisions_url': do_revisions_url}, + {"do_revisions_url": do_revisions_url}, user_message, template_is_setting=True, ) article.stage = submission_models.STAGE_UNDER_REVISION article.save() - kwargs['user_message_content'] = revision_rendered_template - kwargs['revision'] = revision + kwargs["user_message_content"] = revision_rendered_template + kwargs["revision"] = revision event_logic.Events.raise_event( event_logic.Events.ON_REVISIONS_REQUESTED_NOTIFY, **kwargs, ) return redirect( reverse( - 'view_revision', + "view_revision", kwargs={ - 'article_id': article.pk, - 'revision_id': revision.pk, - } + "article_id": article.pk, + "revision_id": revision.pk, + }, ) ) @@ -552,8 +561,8 @@ def handle_decision_action(article, draft, request): def get_access_code(request): - if request.GET.get('access_code'): - access_code = request.GET.get('access_code') + if request.GET.get("access_code"): + access_code = request.GET.get("access_code") else: access_code = None @@ -563,35 +572,37 @@ def get_access_code(request): def quick_assign(request, article, reviewer_user=None): errors = [] try: - default_review_form_id = setting_handler.get_setting('general', - 'default_review_form', - request.journal).processed_value + default_review_form_id = setting_handler.get_setting( + "general", "default_review_form", request.journal + ).processed_value except models.ReviewForm.DoesNotExist: - errors.append('This journal has no default review form.') + errors.append("This journal has no default review form.") try: review_form = models.ReviewForm.objects.get(pk=default_review_form_id) except ValueError: - errors.append('Default review form is not an integer.') + errors.append("Default review form is not an integer.") try: - default_visibility = setting_handler.get_setting('general', - 'default_review_visibility', - request.journal).value - default_due = setting_handler.get_setting('general', - 'default_review_days', - request.journal).value + default_visibility = setting_handler.get_setting( + "general", "default_review_visibility", request.journal + ).value + default_due = setting_handler.get_setting( + "general", "default_review_days", request.journal + ).value except Exception: - errors.append('This journal does not have either default visibilty or default due.') + errors.append( + "This journal does not have either default visibilty or default due." + ) if not reviewer_user: - user_id = request.POST.get('quick_assign') + user_id = request.POST.get("quick_assign") user = core_models.Account.objects.get(pk=user_id) else: user = reviewer_user - if user not in request.journal.users_with_role('reviewer'): - errors.append('This user is not a reviewer for this journal.') + if user not in request.journal.users_with_role("reviewer"): + errors.append("This user is not a reviewer for this journal.") if not errors: new_assignment = models.ReviewAssignment.objects.create( @@ -608,16 +619,24 @@ def quick_assign(request, article, reviewer_user=None): article.stage = submission_models.STAGE_UNDER_REVIEW article.save() - email_content = get_reviewer_notification(request, article, request.user, new_assignment) + email_content = get_reviewer_notification( + request, article, request.user, new_assignment + ) - kwargs = {'user_message_content': email_content, - 'review_assignment': new_assignment, - 'request': request, - 'skip': False, - 'acknowledgement': False} + kwargs = { + "user_message_content": email_content, + "review_assignment": new_assignment, + "request": request, + "skip": False, + "acknowledgement": False, + } - event_logic.Events.raise_event(event_logic.Events.ON_REVIEWER_REQUESTED, **kwargs) - event_logic.Events.raise_event(event_logic.Events.ON_REVIEWER_REQUESTED_ACKNOWLEDGE, **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEWER_REQUESTED, **kwargs + ) + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEWER_REQUESTED_ACKNOWLEDGE, **kwargs + ) return new_assignment @@ -630,38 +649,43 @@ def handle_reviewer_form(request, new_reviewer_form): account = new_reviewer_form.save(commit=False) account.is_active = True account.save() - account.add_account_role('reviewer', request.journal) - messages.add_message(request, messages.INFO, 'A new account has been created.') + account.add_account_role("reviewer", request.journal) + messages.add_message(request, messages.INFO, "A new account has been created.") return account def get_enrollable_users(request): account_roles = core_models.AccountRole.objects.filter( journal=request.journal, - role__slug='reviewer', + role__slug="reviewer", ).prefetch_related( - 'user', + "user", ) users_with_role = [assignment.user.pk for assignment in account_roles] - return core_models.Account.objects.all().order_by( - 'last_name', - ).exclude( - pk__in=users_with_role, + return ( + core_models.Account.objects.all() + .order_by( + "last_name", + ) + .exclude( + pk__in=users_with_role, + ) ) def generate_access_code_url(url_name, assignment, access_code): - - reverse_url = reverse(url_name, kwargs={'assignment_id': assignment.pk}) + reverse_url = reverse(url_name, kwargs={"assignment_id": assignment.pk}) if access_code: - reverse_url = '{reverse_url}?access_code={access_code}'.format(reverse_url=reverse_url, access_code=access_code) + reverse_url = "{reverse_url}?access_code={access_code}".format( + reverse_url=reverse_url, access_code=access_code + ) return reverse_url def render_choices(choices): - c_split = choices.split('|') + c_split = choices.split("|") return [(choice.capitalize(), choice) for choice in c_split] @@ -673,13 +697,17 @@ def serve_review_file(assignment): """ elements = assignment.form.elements.all() document = Document() - document.add_heading('Review #{pk}'.format(pk=assignment.pk), 0) - document.add_heading('Review of `{article_title}`'.format(article_title=assignment.article.title), - level=1) + document.add_heading("Review #{pk}".format(pk=assignment.pk), 0) + document.add_heading( + "Review of `{article_title}`".format(article_title=assignment.article.title), + level=1, + ) document.add_paragraph() - document.add_paragraph('Complete the form below, then upload it under the "FILE UPLOAD" section on your review page' - '. There is no need to complete the form on the web page if you are uploading this ' - 'document.') + document.add_paragraph( + 'Complete the form below, then upload it under the "FILE UPLOAD" section on your review page' + ". There is no need to complete the form on the web page if you are uploading this " + "document." + ) document.add_paragraph() for element in elements: @@ -689,22 +717,22 @@ def serve_review_file(assignment): choices = render_choices(element.choices) table = document.add_table(rows=1, cols=2) hdr_cells = table.rows[0].cells - hdr_cells[0].text = 'Choice' - hdr_cells[1].text = 'Indication' + hdr_cells[0].text = "Choice" + hdr_cells[1].text = "Indication" - for choice in element.choices.split('|'): + for choice in element.choices.split("|"): row_cells = table.add_row().cells row_cells[0].text = str(choice) document.add_paragraph() - filename = '{uuid}.docx'.format(uuid=uuid4()) - filepath = os.path.join(settings.BASE_DIR, 'files', 'temp', filename) + filename = "{uuid}.docx".format(uuid=uuid4()) + filepath = os.path.join(settings.BASE_DIR, "files", "temp", filename) document.save(filepath) return files.serve_temp_file(filepath, filename) def handle_review_file_switch(review, switch): - if switch == 'true': + if switch == "true": review.display_review_file = True else: review.display_review_file = False @@ -717,7 +745,7 @@ def get_reminder_content(reminder_type, article, review_assignment, request): Fetches the right email for a reminder type """ - if reminder_type == 'request': + if reminder_type == "request": return get_reviewer_notification( request, article, @@ -725,7 +753,7 @@ def get_reminder_content(reminder_type, article, review_assignment, request): review_assignment, reminder=reminder_type, ) - elif reminder_type == 'accepted': + elif reminder_type == "accepted": return get_reviewer_notification( request, article, @@ -739,34 +767,37 @@ def send_review_reminder(request, form, review_assignment, reminder_type): """ Sends a reminder email to a reviewer """ - desc = '{sender} sent review reminder of type {type} to {to}'.format( + desc = "{sender} sent review reminder of type {type} to {to}".format( sender=request.user.full_name(), type=reminder_type, - to=review_assignment.reviewer.full_name() + to=review_assignment.reviewer.full_name(), ) - log_dict = {'level': 'Info', - 'action_text': desc, - 'types': 'Review Reminder', - 'target': review_assignment.article} + log_dict = { + "level": "Info", + "action_text": desc, + "types": "Review Reminder", + "target": review_assignment.article, + } notify_helpers.send_email_with_body_from_user( request, - form.cleaned_data['subject'], + form.cleaned_data["subject"], review_assignment.reviewer.email, - form.cleaned_data['body'], - log_dict=log_dict + form.cleaned_data["body"], + log_dict=log_dict, ) def assign_editor( - article, - editor, - assignment_type, - request=None, - skip=True, - automate_email=False, + article, + editor, + assignment_type, + request=None, + skip=True, + automate_email=False, ): from core.forms import SettingEmailForm + assignment, created = models.EditorAssignment.objects.get_or_create( article=article, editor=editor, @@ -785,8 +816,8 @@ def assign_editor( request=request, ) post_data = { - 'subject': form.fields['subject'].initial, - 'body': form.fields['body'].initial, + "subject": form.fields["subject"].initial, + "body": form.fields["body"].initial, } form = SettingEmailForm( post_data, @@ -797,15 +828,16 @@ def assign_editor( if form.is_valid(): kwargs = { - 'email_data': form.as_dataclass(), - 'editor_assignment': assignment, - 'request': request, - 'skip': skip, - 'acknowledgement': False, + "email_data": form.as_dataclass(), + "editor_assignment": assignment, + "request": request, + "skip": skip, + "acknowledgement": False, } event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_ASSIGNED, - task_object=article, **kwargs, + task_object=article, + **kwargs, ) if not skip: event_logic.Events.raise_event( @@ -820,32 +852,32 @@ def process_reviewer_csv(path, request, article, form): Iterates through a CSV c """ try: - csv_file = open(path, 'r', encoding="utf-8-sig") + csv_file = open(path, "r", encoding="utf-8-sig") reader = csv.DictReader(csv_file) reviewers = [] for row in reader: try: - country = core_models.Country.objects.get(code=row.get('country')) + country = core_models.Country.objects.get(code=row.get("country")) except core_models.Country.DoesNotExist: country = None reviewer, created = core_models.Account.objects.get_or_create( - email=row.get('email_address'), + email=row.get("email_address"), defaults={ - 'salutation': row.get('salutation', ''), - 'first_name': row.get('firstname', ''), - 'middle_name': row.get('middlename', ''), - 'last_name': row.get('lastname', ''), - 'department': row.get('department', ''), - 'institution': row.get('institution', ''), - 'country': country, - 'is_active': True, - } + "salutation": row.get("salutation", ""), + "first_name": row.get("firstname", ""), + "middle_name": row.get("middlename", ""), + "last_name": row.get("lastname", ""), + "department": row.get("department", ""), + "institution": row.get("institution", ""), + "country": country, + "is_active": True, + }, ) try: - review_interests = row.get('interests') - re.split('[,;]+', review_interests) + review_interests = row.get("interests") + re.split("[,;]+", review_interests) except (IndexError, AttributeError): review_interests = [] @@ -854,41 +886,43 @@ def process_reviewer_csv(path, request, article, form): reviewer.interest.add(interest) # Add the reviewer role - reviewer.add_account_role('reviewer', request.journal) + reviewer.add_account_role("reviewer", request.journal) review_assignment, c = models.ReviewAssignment.objects.get_or_create( article=article, reviewer=reviewer, review_round=article.current_review_round_object(), defaults={ - 'editor': request.user, - 'date_due': form.cleaned_data.get('date_due'), - 'form': form.cleaned_data.get('form'), - 'visibility': form.cleaned_data.get('visibility'), - 'access_code': uuid4(), - } + "editor": request.user, + "date_due": form.cleaned_data.get("date_due"), + "form": form.cleaned_data.get("form"), + "visibility": form.cleaned_data.get("visibility"), + "access_code": uuid4(), + }, ) review_url = get_review_url(request, review_assignment) html = render_template.get_message_content( request=request, context={ - 'article': article, - 'editor': request.user, - 'review_assignment': review_assignment, - 'review_url': review_url, - 'article_details': get_article_details_for_review(article), - 'reason': row.get('reason') + "article": article, + "editor": request.user, + "review_assignment": review_assignment, + "review_url": review_url, + "article_details": get_article_details_for_review(article), + "reason": row.get("reason"), }, - template=form.cleaned_data.get('template'), + template=form.cleaned_data.get("template"), template_is_setting=True, ) # finally, call event - kwargs = {'user_message_content': html, - 'review_assignment': review_assignment, - 'request': request, - 'skip': False, - 'acknowledgement': True} + kwargs = { + "user_message_content": html, + "review_assignment": review_assignment, + "request": request, + "skip": False, + "acknowledgement": True, + } event_logic.Events.raise_event( event_logic.Events.ON_REVIEWER_REQUESTED_ACKNOWLEDGE, @@ -897,9 +931,9 @@ def process_reviewer_csv(path, request, article, form): reviewers.append( { - 'account': reviewer, - 'reason': row.get('reason'), - 'review_assignment': review_assignment, + "account": reviewer, + "reason": row.get("reason"), + "review_assignment": review_assignment, } ) return reviewers, None @@ -908,13 +942,13 @@ def process_reviewer_csv(path, request, article, form): def handle_response_letter_upload(request, revision_request): - response_letter = request.FILES.get('response_letter') + response_letter = request.FILES.get("response_letter") if response_letter: file = files.save_file_to_article( response_letter, revision_request.article, owner=request.user, - label='Response Letter', + label="Response Letter", ) revision_request.response_letter = file revision_request.save() @@ -923,7 +957,7 @@ def handle_response_letter_upload(request, revision_request): messages.add_message( request, messages.WARNING, - 'No response letter selected.', + "No response letter selected.", ) return diff --git a/src/review/migrations/0001_initial.py b/src/review/migrations/0001_initial.py index be3de63f34..1bbeb48fa5 100755 --- a/src/review/migrations/0001_initial.py +++ b/src/review/migrations/0001_initial.py @@ -10,211 +10,578 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('submission', '0001_initial'), - ('core', '0001_initial'), - ('journal', '0002_auto_20170711_1203'), + ("submission", "0001_initial"), + ("core", "0001_initial"), + ("journal", "0002_auto_20170711_1203"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='DecisionDraft', + name="DecisionDraft", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('decision', models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=100)), - ('message_to_editor', models.TextField(blank=True, null=True)), - ('email_message', models.TextField(blank=True, null=True)), - ('drafted', models.DateTimeField(auto_now=True)), - ('editor_decision', models.CharField(blank=True, choices=[('accept', 'Accept'), ('decline', 'Decline')], max_length=20, null=True)), - ('closed', models.BooleanField(default=False)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('section_editor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='draft_section_editor', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "decision", + models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=100, + ), + ), + ("message_to_editor", models.TextField(blank=True, null=True)), + ("email_message", models.TextField(blank=True, null=True)), + ("drafted", models.DateTimeField(auto_now=True)), + ( + "editor_decision", + models.CharField( + blank=True, + choices=[("accept", "Accept"), ("decline", "Decline")], + max_length=20, + null=True, + ), + ), + ("closed", models.BooleanField(default=False)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "section_editor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="draft_section_editor", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='EditorAssignment', + name="EditorAssignment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('editor_type', models.CharField(choices=[('editor', 'Editor'), ('section-editor', 'Section Editor')], max_length=20)), - ('assigned', models.DateTimeField(default=django.utils.timezone.now)), - ('notified', models.BooleanField(default=False)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('editor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "editor_type", + models.CharField( + choices=[ + ("editor", "Editor"), + ("section-editor", "Section Editor"), + ], + max_length=20, + ), + ), + ("assigned", models.DateTimeField(default=django.utils.timezone.now)), + ("notified", models.BooleanField(default=False)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "editor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='EditorOverride', + name="EditorOverride", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('overwritten', models.DateTimeField(default=django.utils.timezone.now)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('editor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "overwritten", + models.DateTimeField(default=django.utils.timezone.now), + ), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "editor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='ReviewAssignment', + name="ReviewAssignment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('decision', models.CharField(blank=True, choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=20, null=True)), - ('competing_interests', models.TextField(blank=True, help_text="If any of the authors or editors have any competing interests please add them here. EG. 'This study was paid for by corp xyz.'.", null=True)), - ('review_type', models.CharField(choices=[('traditional', 'Traditional'), ('annotation', 'Annotation')], default='traditional', help_text='Traditional review uses a form set, annotation review is freeform using hypothes.is', max_length=20)), - ('visibility', models.CharField(choices=[('open', 'Open'), ('blind', 'Blind'), ('double-blind', 'Double Blind')], default='blind', max_length=20)), - ('access_code', models.CharField(blank=True, max_length=100, null=True)), - ('date_requested', models.DateTimeField(auto_now_add=True)), - ('date_due', models.DateField()), - ('date_accepted', models.DateTimeField(blank=True, null=True)), - ('date_declined', models.DateTimeField(blank=True, null=True)), - ('date_complete', models.DateTimeField(blank=True, null=True)), - ('date_reminded', models.DateField(blank=True, null=True)), - ('is_complete', models.BooleanField(default=False)), - ('for_author_consumption', models.BooleanField(default=False)), - ('suggested_reviewers', models.TextField(blank=True, null=True)), - ('comments_for_editor', models.TextField(blank=True, help_text='If you have any comments for the Editor you can add them here, these will not be shared with the Author.', null=True, verbose_name='Comments for the Editor')), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('editor', models.ForeignKey(help_text='Editor requesting the review', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='editor', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "decision", + models.CharField( + blank=True, + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=20, + null=True, + ), + ), + ( + "competing_interests", + models.TextField( + blank=True, + help_text="If any of the authors or editors have any competing interests please add them here. EG. 'This study was paid for by corp xyz.'.", + null=True, + ), + ), + ( + "review_type", + models.CharField( + choices=[ + ("traditional", "Traditional"), + ("annotation", "Annotation"), + ], + default="traditional", + help_text="Traditional review uses a form set, annotation review is freeform using hypothes.is", + max_length=20, + ), + ), + ( + "visibility", + models.CharField( + choices=[ + ("open", "Open"), + ("blind", "Blind"), + ("double-blind", "Double Blind"), + ], + default="blind", + max_length=20, + ), + ), + ( + "access_code", + models.CharField(blank=True, max_length=100, null=True), + ), + ("date_requested", models.DateTimeField(auto_now_add=True)), + ("date_due", models.DateField()), + ("date_accepted", models.DateTimeField(blank=True, null=True)), + ("date_declined", models.DateTimeField(blank=True, null=True)), + ("date_complete", models.DateTimeField(blank=True, null=True)), + ("date_reminded", models.DateField(blank=True, null=True)), + ("is_complete", models.BooleanField(default=False)), + ("for_author_consumption", models.BooleanField(default=False)), + ("suggested_reviewers", models.TextField(blank=True, null=True)), + ( + "comments_for_editor", + models.TextField( + blank=True, + help_text="If you have any comments for the Editor you can add them here, these will not be shared with the Author.", + null=True, + verbose_name="Comments for the Editor", + ), + ), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "editor", + models.ForeignKey( + help_text="Editor requesting the review", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="editor", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='ReviewAssignmentAnswer', + name="ReviewAssignmentAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField()), - ('edited_answer', models.TextField(blank=True, null=True)), - ('author_can_see', models.BooleanField(default=True)), - ('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewAssignment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("answer", models.TextField()), + ("edited_answer", models.TextField(blank=True, null=True)), + ("author_can_see", models.BooleanField(default=True)), + ( + "assignment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewAssignment", + ), + ), ], ), migrations.CreateModel( - name='ReviewerRating', + name="ReviewerRating", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rating', models.IntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(10)])), - ('assignment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewAssignment')), - ('rater', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "rating", + models.IntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(10), + ] + ), + ), + ( + "assignment", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewAssignment", + ), + ), + ( + "rater", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='ReviewForm', + name="ReviewForm", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('slug', models.SlugField(max_length=200)), - ('intro', models.TextField(help_text='Message displayed at the start of the review form.')), - ('thanks', models.TextField(help_text='Message displayed after the reviewer is finished.')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ("slug", models.SlugField(max_length=200)), + ( + "intro", + models.TextField( + help_text="Message displayed at the start of the review form." + ), + ), + ( + "thanks", + models.TextField( + help_text="Message displayed after the reviewer is finished." + ), + ), ], ), migrations.CreateModel( - name='ReviewFormAnswer', + name="ReviewFormAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("answer", models.TextField()), ], ), migrations.CreateModel( - name='ReviewFormElement', + name="ReviewFormElement", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('kind', models.CharField(choices=[('text', 'Text Field'), ('textarea', 'Text Area'), ('check', 'Check Box'), ('select', 'Select'), ('email', 'Email'), ('upload', 'Upload'), ('date', 'Date')], max_length=50)), - ('choices', models.CharField(blank=True, help_text='Seperate choices with the bar | character.', max_length=1000, null=True)), - ('required', models.BooleanField(default=True)), - ('order', models.IntegerField()), - ('width', models.CharField(choices=[('large-4 columns', 'third'), ('large-6 columns', 'half'), ('large-12 columns', 'full')], max_length=20)), - ('help_text', models.TextField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ( + "kind", + models.CharField( + choices=[ + ("text", "Text Field"), + ("textarea", "Text Area"), + ("check", "Check Box"), + ("select", "Select"), + ("email", "Email"), + ("upload", "Upload"), + ("date", "Date"), + ], + max_length=50, + ), + ), + ( + "choices", + models.CharField( + blank=True, + help_text="Seperate choices with the bar | character.", + max_length=1000, + null=True, + ), + ), + ("required", models.BooleanField(default=True)), + ("order", models.IntegerField()), + ( + "width", + models.CharField( + choices=[ + ("large-4 columns", "third"), + ("large-6 columns", "half"), + ("large-12 columns", "full"), + ], + max_length=20, + ), + ), + ("help_text", models.TextField()), ], ), migrations.CreateModel( - name='ReviewRound', + name="ReviewRound", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('round_number', models.IntegerField()), - ('date_started', models.DateTimeField(auto_now_add=True)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('review_files', models.ManyToManyField(to='core.File')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("round_number", models.IntegerField()), + ("date_started", models.DateTimeField(auto_now_add=True)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ("review_files", models.ManyToManyField(to="core.File")), ], options={ - 'ordering': ('-round_number',), + "ordering": ("-round_number",), }, ), migrations.CreateModel( - name='RevisionAction', + name="RevisionAction", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField()), - ('logged', models.DateTimeField(blank=True, default=None, null=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.TextField()), + ("logged", models.DateTimeField(blank=True, default=None, null=True)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='RevisionRequest', + name="RevisionRequest", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('editor_note', models.TextField()), - ('author_note', models.TextField(blank=True, null=True, verbose_name='Covering Letter')), - ('type', models.CharField(choices=[('minor_revisions', 'Minor Revisions'), ('major_revisions', 'Major Revisions')], default='minor_revisions', max_length=20)), - ('date_requested', models.DateField(default=django.utils.timezone.now)), - ('date_due', models.DateField()), - ('date_completed', models.DateTimeField(blank=True, null=True)), - ('actions', models.ManyToManyField(to='review.RevisionAction')), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('editor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("editor_note", models.TextField()), + ( + "author_note", + models.TextField( + blank=True, null=True, verbose_name="Covering Letter" + ), + ), + ( + "type", + models.CharField( + choices=[ + ("minor_revisions", "Minor Revisions"), + ("major_revisions", "Major Revisions"), + ], + default="minor_revisions", + max_length=20, + ), + ), + ("date_requested", models.DateField(default=django.utils.timezone.now)), + ("date_due", models.DateField()), + ("date_completed", models.DateTimeField(blank=True, null=True)), + ("actions", models.ManyToManyField(to="review.RevisionAction")), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "editor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='reviewformanswer', - name='form_element', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewFormElement'), + model_name="reviewformanswer", + name="form_element", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewFormElement", + ), ), migrations.AddField( - model_name='reviewformanswer', - name='review_assignment', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewAssignment'), + model_name="reviewformanswer", + name="review_assignment", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewAssignment", + ), ), migrations.AddField( - model_name='reviewform', - name='elements', - field=models.ManyToManyField(to='review.ReviewFormElement'), + model_name="reviewform", + name="elements", + field=models.ManyToManyField(to="review.ReviewFormElement"), ), migrations.AddField( - model_name='reviewform', - name='journal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="reviewform", + name="journal", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), migrations.AddField( - model_name='reviewassignmentanswer', - name='element', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewFormElement'), + model_name="reviewassignmentanswer", + name="element", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewFormElement", + ), ), migrations.AddField( - model_name='reviewassignment', - name='form', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.ReviewForm'), + model_name="reviewassignment", + name="form", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="review.ReviewForm" + ), ), migrations.AddField( - model_name='reviewassignment', - name='review_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.File'), + model_name="reviewassignment", + name="review_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.File", + ), ), migrations.AddField( - model_name='reviewassignment', - name='review_round', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='review.ReviewRound'), + model_name="reviewassignment", + name="review_round", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="review.ReviewRound", + ), ), migrations.AddField( - model_name='reviewassignment', - name='reviewer', - field=models.ForeignKey(help_text='User to undertake the review', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reviewer', to=settings.AUTH_USER_MODEL), + model_name="reviewassignment", + name="reviewer", + field=models.ForeignKey( + help_text="User to undertake the review", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="reviewer", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterUniqueTogether( - name='reviewround', - unique_together=set([('article', 'round_number')]), + name="reviewround", + unique_together=set([("article", "round_number")]), ), migrations.AlterUniqueTogether( - name='editorassignment', - unique_together=set([('article', 'editor')]), + name="editorassignment", + unique_together=set([("article", "editor")]), ), ] diff --git a/src/review/migrations/0002_reviewformelement_default_visibility.py b/src/review/migrations/0002_reviewformelement_default_visibility.py index a4787d23a1..273e6c2d4a 100755 --- a/src/review/migrations/0002_reviewformelement_default_visibility.py +++ b/src/review/migrations/0002_reviewformelement_default_visibility.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0001_initial'), + ("review", "0001_initial"), ] operations = [ migrations.AddField( - model_name='reviewformelement', - name='default_visibility', - field=models.BooleanField(default=True, help_text='If true, this setting will be availableto the author automatically, if false it willbe hidden to the author by default.'), + model_name="reviewformelement", + name="default_visibility", + field=models.BooleanField( + default=True, + help_text="If true, this setting will be availableto the author automatically, if false it willbe hidden to the author by default.", + ), ), ] diff --git a/src/review/migrations/0003_auto_20180208_1225.py b/src/review/migrations/0003_auto_20180208_1225.py index adefe6a884..2870a8cd40 100644 --- a/src/review/migrations/0003_auto_20180208_1225.py +++ b/src/review/migrations/0003_auto_20180208_1225.py @@ -6,20 +6,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0002_reviewformelement_default_visibility'), + ("review", "0002_reviewformelement_default_visibility"), ] operations = [ migrations.AlterField( - model_name='reviewassignment', - name='review_type', - field=models.CharField(choices=[('traditional', 'Traditional')], default='traditional', help_text='Currently only traditional, form based, review is available.', max_length=20), + model_name="reviewassignment", + name="review_type", + field=models.CharField( + choices=[("traditional", "Traditional")], + default="traditional", + help_text="Currently only traditional, form based, review is available.", + max_length=20, + ), ), migrations.AlterField( - model_name='reviewformelement', - name='default_visibility', - field=models.BooleanField(default=True, help_text='If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.'), + model_name="reviewformelement", + name="default_visibility", + field=models.BooleanField( + default=True, + help_text="If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.", + ), ), ] diff --git a/src/review/migrations/0003_auto_20180614_1039.py b/src/review/migrations/0003_auto_20180614_1039.py index 6f99a10d90..a4b396a24d 100644 --- a/src/review/migrations/0003_auto_20180614_1039.py +++ b/src/review/migrations/0003_auto_20180614_1039.py @@ -6,25 +6,41 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0002_reviewformelement_default_visibility'), + ("review", "0002_reviewformelement_default_visibility"), ] operations = [ migrations.AlterField( - model_name='reviewassignment', - name='review_type', - field=models.CharField(choices=[('traditional', 'Traditional')], default='traditional', help_text='Currently only traditional, form based, review is available.', max_length=20), + model_name="reviewassignment", + name="review_type", + field=models.CharField( + choices=[("traditional", "Traditional")], + default="traditional", + help_text="Currently only traditional, form based, review is available.", + max_length=20, + ), ), migrations.AlterField( - model_name='reviewassignment', - name='visibility', - field=models.CharField(choices=[('open', 'Open'), ('blind', 'Single Anonymous'), ('double-blind', 'Double Anonymous')], default='double-blind', max_length=20, verbose_name="Anonymity"), + model_name="reviewassignment", + name="visibility", + field=models.CharField( + choices=[ + ("open", "Open"), + ("blind", "Single Anonymous"), + ("double-blind", "Double Anonymous"), + ], + default="double-blind", + max_length=20, + verbose_name="Anonymity", + ), ), migrations.AlterField( - model_name='reviewformelement', - name='default_visibility', - field=models.BooleanField(default=True, help_text='If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.'), + model_name="reviewformelement", + name="default_visibility", + field=models.BooleanField( + default=True, + help_text="If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.", + ), ), ] diff --git a/src/review/migrations/0004_reviewassignment_display_review_file.py b/src/review/migrations/0004_reviewassignment_display_review_file.py index 528d92774a..25c7b95db5 100644 --- a/src/review/migrations/0004_reviewassignment_display_review_file.py +++ b/src/review/migrations/0004_reviewassignment_display_review_file.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0003_auto_20180208_1225'), + ("review", "0003_auto_20180208_1225"), ] operations = [ migrations.AddField( - model_name='reviewassignment', - name='display_review_file', + model_name="reviewassignment", + name="display_review_file", field=models.BooleanField(default=False), ), ] diff --git a/src/review/migrations/0005_merge_20180716_1312.py b/src/review/migrations/0005_merge_20180716_1312.py index 2ebad23aa6..9050928c57 100644 --- a/src/review/migrations/0005_merge_20180716_1312.py +++ b/src/review/migrations/0005_merge_20180716_1312.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0003_auto_20180614_1039'), - ('review', '0004_reviewassignment_display_review_file'), + ("review", "0003_auto_20180614_1039"), + ("review", "0004_reviewassignment_display_review_file"), ] - operations = [ - ] + operations = [] diff --git a/src/review/migrations/0006_auto_20200529_1415.py b/src/review/migrations/0006_auto_20200529_1415.py index f93ce450c0..99171b6598 100644 --- a/src/review/migrations/0006_auto_20200529_1415.py +++ b/src/review/migrations/0006_auto_20200529_1415.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0005_merge_20180716_1312'), + ("review", "0005_merge_20180716_1312"), ] operations = [ migrations.AlterField( - model_name='reviewassignment', - name='comments_for_editor', - field=models.TextField(blank=True, help_text='If you have any comments for the Editor you can add them here; these will not be shared with the Author.', null=True, verbose_name='Comments for the Editor'), + model_name="reviewassignment", + name="comments_for_editor", + field=models.TextField( + blank=True, + help_text="If you have any comments for the Editor you can add them here; these will not be shared with the Author.", + null=True, + verbose_name="Comments for the Editor", + ), ), ] diff --git a/src/review/migrations/0007_null_help_text.py b/src/review/migrations/0007_null_help_text.py index 2a13b88018..624f4c6def 100644 --- a/src/review/migrations/0007_null_help_text.py +++ b/src/review/migrations/0007_null_help_text.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0006_auto_20200529_1415'), + ("review", "0006_auto_20200529_1415"), ] operations = [ migrations.AlterField( - model_name='reviewformelement', - name='help_text', + model_name="reviewformelement", + name="help_text", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/review/migrations/0008_reviewform_deleted.py b/src/review/migrations/0008_reviewform_deleted.py index 79386bf876..e7310bcb08 100644 --- a/src/review/migrations/0008_reviewform_deleted.py +++ b/src/review/migrations/0008_reviewform_deleted.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0007_null_help_text'), + ("review", "0007_null_help_text"), ] operations = [ migrations.AddField( - model_name='reviewform', - name='deleted', + model_name="reviewform", + name="deleted", field=models.BooleanField(default=False), ), ] diff --git a/src/review/migrations/0009_review_form_element_order.py b/src/review/migrations/0009_review_form_element_order.py index 92cc772b6c..f411e385bc 100644 --- a/src/review/migrations/0009_review_form_element_order.py +++ b/src/review/migrations/0009_review_form_element_order.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0008_reviewform_deleted'), + ("review", "0008_reviewform_deleted"), ] operations = [ migrations.AlterModelOptions( - name='reviewformelement', - options={'ordering': ('order', 'name')}, + name="reviewformelement", + options={"ordering": ("order", "name")}, ), ] diff --git a/src/review/migrations/0010_answer_not_required.py b/src/review/migrations/0010_answer_not_required.py index 5faaa8ff1e..a2a204b137 100644 --- a/src/review/migrations/0010_answer_not_required.py +++ b/src/review/migrations/0010_answer_not_required.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0009_review_form_element_order'), + ("review", "0009_review_form_element_order"), ] operations = [ migrations.AlterField( - model_name='reviewassignmentanswer', - name='answer', + model_name="reviewassignmentanswer", + name="answer", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/review/migrations/0011_keep_answers_on_form_delete.py b/src/review/migrations/0011_keep_answers_on_form_delete.py index 4b560c09b4..5d96bb2f37 100644 --- a/src/review/migrations/0011_keep_answers_on_form_delete.py +++ b/src/review/migrations/0011_keep_answers_on_form_delete.py @@ -39,58 +39,125 @@ def frozen_element_to_element(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('review', '0010_answer_not_required'), + ("review", "0010_answer_not_required"), ] operations = [ migrations.AlterField( - model_name='reviewassignmentanswer', - name='element', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='review.ReviewFormElement'), + model_name="reviewassignmentanswer", + name="element", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="review.ReviewFormElement", + ), ), migrations.CreateModel( - name='FrozenReviewFormElement', + name="FrozenReviewFormElement", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('kind', models.CharField(choices=[('text', 'Text Field'), ('textarea', 'Text Area'), ('check', 'Check Box'), ('select', 'Select'), ('email', 'Email'), ('upload', 'Upload'), ('date', 'Date')], max_length=50)), - ('choices', models.CharField(blank=True, help_text='Seperate choices with the bar | character.', max_length=1000, null=True)), - ('required', models.BooleanField(default=True)), - ('order', models.IntegerField()), - ('width', models.CharField(choices=[('large-4 columns', 'third'), ('large-6 columns', 'half'), ('large-12 columns', 'full')], max_length=20)), - ('help_text', models.TextField(blank=True, null=True)), - ('default_visibility', models.BooleanField(default=True, help_text='If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ( + "kind", + models.CharField( + choices=[ + ("text", "Text Field"), + ("textarea", "Text Area"), + ("check", "Check Box"), + ("select", "Select"), + ("email", "Email"), + ("upload", "Upload"), + ("date", "Date"), + ], + max_length=50, + ), + ), + ( + "choices", + models.CharField( + blank=True, + help_text="Seperate choices with the bar | character.", + max_length=1000, + null=True, + ), + ), + ("required", models.BooleanField(default=True)), + ("order", models.IntegerField()), + ( + "width", + models.CharField( + choices=[ + ("large-4 columns", "third"), + ("large-6 columns", "half"), + ("large-12 columns", "full"), + ], + max_length=20, + ), + ), + ("help_text", models.TextField(blank=True, null=True)), + ( + "default_visibility", + models.BooleanField( + default=True, + help_text="If true, this setting will be available to the author automatically, if false it willbe hidden to the author by default.", + ), + ), ], options={ - 'ordering': ('order', 'name'), - 'abstract': False, + "ordering": ("order", "name"), + "abstract": False, }, ), migrations.AddField( - model_name='frozenreviewformelement', - name='answer', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='frozen_element', to='review.ReviewAssignmentAnswer'), + model_name="frozenreviewformelement", + name="answer", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="frozen_element", + to="review.ReviewAssignmentAnswer", + ), ), migrations.AddField( - model_name='reviewassignmentanswer', - name='original_element', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='review.ReviewFormElement'), + model_name="reviewassignmentanswer", + name="original_element", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="review.ReviewFormElement", + ), ), migrations.AddField( - model_name='frozenreviewformelement', - name='form_element', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='review.ReviewFormElement'), + model_name="frozenreviewformelement", + name="form_element", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="review.ReviewFormElement", + ), + ), + migrations.RunPython( + element_to_frozen_element, reverse_code=frozen_element_to_element ), - migrations.RunPython(element_to_frozen_element, reverse_code=frozen_element_to_element), migrations.AlterField( - model_name='reviewassignment', - name='form', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='review.ReviewForm'), + model_name="reviewassignment", + name="form", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="review.ReviewForm", + ), ), migrations.RemoveField( - model_name='reviewassignmentanswer', - name='element', + model_name="reviewassignmentanswer", + name="element", ), - ] \ No newline at end of file + ] diff --git a/src/review/migrations/0012_auto_20210330_1138.py b/src/review/migrations/0012_auto_20210330_1138.py index 940d0b996a..27f9ed0522 100644 --- a/src/review/migrations/0012_auto_20210330_1138.py +++ b/src/review/migrations/0012_auto_20210330_1138.py @@ -6,30 +6,57 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0011_keep_answers_on_form_delete'), + ("review", "0011_keep_answers_on_form_delete"), ] operations = [ migrations.AddField( - model_name='decisiondraft', - name='revision_request_due_date', - field=models.DateTimeField(blank=True, help_text='Stores a due date for a Drafted Revision Request.', null=True), + model_name="decisiondraft", + name="revision_request_due_date", + field=models.DateTimeField( + blank=True, + help_text="Stores a due date for a Drafted Revision Request.", + null=True, + ), ), migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=100, verbose_name='Recommendation'), + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=100, + verbose_name="Recommendation", + ), ), migrations.AlterField( - model_name='decisiondraft', - name='email_message', - field=models.TextField(blank=True, help_text='This is a draft of the email that will be sent to the author.', null=True), + model_name="decisiondraft", + name="email_message", + field=models.TextField( + blank=True, + help_text="This is a draft of the email that will be sent to the author.", + null=True, + ), ), migrations.AlterField( - model_name='reviewassignment', - name='decision', - field=models.CharField(blank=True, choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=20, null=True, verbose_name='Recommendation'), + model_name="reviewassignment", + name="decision", + field=models.CharField( + blank=True, + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=20, + null=True, + verbose_name="Recommendation", + ), ), ] diff --git a/src/review/migrations/0013_auto_20210331_1052.py b/src/review/migrations/0013_auto_20210331_1052.py index 2f8f31bfee..b060163d2b 100644 --- a/src/review/migrations/0013_auto_20210331_1052.py +++ b/src/review/migrations/0013_auto_20210331_1052.py @@ -6,19 +6,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0012_auto_20210330_1138'), + ("review", "0012_auto_20210330_1138"), ] operations = [ migrations.RemoveField( - model_name='decisiondraft', - name='closed', + model_name="decisiondraft", + name="closed", ), migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=100), + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=100, + ), ), ] diff --git a/src/review/migrations/0014_auto_20210414_0802.py b/src/review/migrations/0014_auto_20210414_0802.py index b2a074b1b6..34265740b7 100644 --- a/src/review/migrations/0014_auto_20210414_0802.py +++ b/src/review/migrations/0014_auto_20210414_0802.py @@ -8,31 +8,54 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('review', '0013_auto_20210331_1052'), + ("review", "0013_auto_20210331_1052"), ] operations = [ migrations.AddField( - model_name='decisiondraft', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='draft_editor', to=settings.AUTH_USER_MODEL), + model_name="decisiondraft", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="draft_editor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject')], max_length=100, verbose_name='Draft Decision'), + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ], + max_length=100, + verbose_name="Draft Decision", + ), ), migrations.AlterField( - model_name='decisiondraft', - name='email_message', - field=models.TextField(blank=True, help_text='This is a draft of the email that will be sent to the author. Your editor will check this.', null=True, verbose_name='Draft Email to Author'), + model_name="decisiondraft", + name="email_message", + field=models.TextField( + blank=True, + help_text="This is a draft of the email that will be sent to the author. Your editor will check this.", + null=True, + verbose_name="Draft Email to Author", + ), ), migrations.AlterField( - model_name='decisiondraft', - name='message_to_editor', - field=models.TextField(blank=True, help_text='This is the email that will be sent to the editor notifying them that you are logging your draft decision.', null=True, verbose_name='Email to Editor'), + model_name="decisiondraft", + name="message_to_editor", + field=models.TextField( + blank=True, + help_text="This is the email that will be sent to the editor notifying them that you are logging your draft decision.", + null=True, + verbose_name="Email to Editor", + ), ), ] diff --git a/src/review/migrations/0015_decisiondraft_editor_decline_rationale.py b/src/review/migrations/0015_decisiondraft_editor_decline_rationale.py index a14ad3fb44..26fccc0382 100644 --- a/src/review/migrations/0015_decisiondraft_editor_decline_rationale.py +++ b/src/review/migrations/0015_decisiondraft_editor_decline_rationale.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0014_auto_20210414_0802'), + ("review", "0014_auto_20210414_0802"), ] operations = [ migrations.AddField( - model_name='decisiondraft', - name='editor_decline_rationale', - field=models.TextField(blank=True, help_text='Provide the section editor with a rationale for declining their drafted decision.', null=True, verbose_name='Rationale for Declining Draft Decision'), + model_name="decisiondraft", + name="editor_decline_rationale", + field=models.TextField( + blank=True, + help_text="Provide the section editor with a rationale for declining their drafted decision.", + null=True, + verbose_name="Rationale for Declining Draft Decision", + ), ), ] diff --git a/src/review/migrations/0016_auto_20210510_0957.py b/src/review/migrations/0016_auto_20210510_0957.py index 1895bc763a..aa15bb9f6f 100644 --- a/src/review/migrations/0016_auto_20210510_0957.py +++ b/src/review/migrations/0016_auto_20210510_0957.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0015_decisiondraft_editor_decline_rationale'), + ("review", "0015_decisiondraft_editor_decline_rationale"), ] operations = [ migrations.AlterField( - model_name='revisionrequest', - name='author_note', - field=models.TextField(blank=True, help_text='You can add an optional covering letter to the editor with details of the changes that you have made to your revised manuscript.', null=True, verbose_name='Covering Letter'), + model_name="revisionrequest", + name="author_note", + field=models.TextField( + blank=True, + help_text="You can add an optional covering letter to the editor with details of the changes that you have made to your revised manuscript.", + null=True, + verbose_name="Covering Letter", + ), ), ] diff --git a/src/review/migrations/0017_reviewassignment_display_public.py b/src/review/migrations/0017_reviewassignment_display_public.py index 550bf8aac1..187f3493e9 100644 --- a/src/review/migrations/0017_reviewassignment_display_public.py +++ b/src/review/migrations/0017_reviewassignment_display_public.py @@ -7,26 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0016_auto_20210510_0957'), + ("review", "0016_auto_20210510_0957"), ] operations = [ migrations.AddField( - model_name='reviewassignment', - name='display_public', - field=models.BooleanField(default=False, help_text='Whether this review should be publicly displayed.'), + model_name="reviewassignment", + name="display_public", + field=models.BooleanField( + default=False, + help_text="Whether this review should be publicly displayed.", + ), ), migrations.AddField( - model_name='reviewassignment', - name='permission_to_make_public', - field=models.BooleanField(default=False, - help_text='This journal has a policy of sharing reviews openly alongside the published article to aid in transparency. If you give permission here and the article is published, your name and review will be visible.'), + model_name="reviewassignment", + name="permission_to_make_public", + field=models.BooleanField( + default=False, + help_text="This journal has a policy of sharing reviews openly alongside the published article to aid in transparency. If you give permission here and the article is published, your name and review will be visible.", + ), ), migrations.AlterField( - model_name='revisionrequest', - name='date_requested', + model_name="revisionrequest", + name="date_requested", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/src/review/migrations/0018_auto_20230120_1546.py b/src/review/migrations/0018_auto_20230120_1546.py index 64fda52b35..2006342c26 100644 --- a/src/review/migrations/0018_auto_20230120_1546.py +++ b/src/review/migrations/0018_auto_20230120_1546.py @@ -6,47 +6,78 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_auto_20230120_1546'), + ("core", "0078_auto_20230120_1546"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('review', '0017_reviewassignment_display_public'), + ("review", "0017_reviewassignment_display_public"), ] operations = [ migrations.AlterField( - model_name='decisiondraft', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='draft_editor', to=settings.AUTH_USER_MODEL), + model_name="decisiondraft", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="draft_editor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='decisiondraft', - name='section_editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='draft_section_editor', to=settings.AUTH_USER_MODEL), + model_name="decisiondraft", + name="section_editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="draft_section_editor", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='reviewassignment', - name='review_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.file'), + model_name="reviewassignment", + name="review_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.file", + ), ), migrations.AlterField( - model_name='reviewassignment', - name='review_round', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='review.reviewround'), + model_name="reviewassignment", + name="review_round", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="review.reviewround", + ), ), migrations.AlterField( - model_name='reviewerrating', - name='rater', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="reviewerrating", + name="rater", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='revisionaction', - name='user', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="revisionaction", + name="user", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='revisionrequest', - name='editor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="revisionrequest", + name="editor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/review/migrations/0019_auto_20230418_1113.py b/src/review/migrations/0019_auto_20230418_1113.py index a89ecda995..1887184eaa 100644 --- a/src/review/migrations/0019_auto_20230418_1113.py +++ b/src/review/migrations/0019_auto_20230418_1113.py @@ -4,20 +4,41 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0018_auto_20230120_1546'), + ("review", "0018_auto_20230120_1546"), ] operations = [ migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject'), ('none', 'No Recommendation')], max_length=100, verbose_name='Draft Decision'), + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ("none", "No Recommendation"), + ], + max_length=100, + verbose_name="Draft Decision", + ), ), migrations.AlterField( - model_name='reviewassignment', - name='decision', - field=models.CharField(blank=True, choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('reject', 'Reject'), ('none', 'No Recommendation')], max_length=20, null=True, verbose_name='Recommendation'), + model_name="reviewassignment", + name="decision", + field=models.CharField( + blank=True, + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("reject", "Reject"), + ("none", "No Recommendation"), + ], + max_length=20, + null=True, + verbose_name="Recommendation", + ), ), ] diff --git a/src/review/migrations/0019_decision_decline_rewording.py b/src/review/migrations/0019_decision_decline_rewording.py index b99fb09452..760755f3f8 100644 --- a/src/review/migrations/0019_decision_decline_rewording.py +++ b/src/review/migrations/0019_decision_decline_rewording.py @@ -22,33 +22,90 @@ def decline_to_reject(apps, schema_editor): DecisionDraft.objects.filter(decision="decline").update(decision="reject") - class Migration(migrations.Migration): - dependencies = [ - ('review', '0018_auto_20230120_1546'), + ("review", "0018_auto_20230120_1546"), ] operations = [ migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[(review.const.EditorialDecisions['ACCEPT'], 'Accept Without Revisions'), (review.const.EditorialDecisions['MINOR_REVISIONS'], 'Minor Revisions Required'), (review.const.EditorialDecisions['MAJOR_REVISIONS'], 'Major Revisions Required'), (review.const.EditorialDecisions['DECLINE'], 'Reject')], max_length=100, verbose_name='Draft Decision'), + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ( + review.const.EditorialDecisions["ACCEPT"], + "Accept Without Revisions", + ), + ( + review.const.EditorialDecisions["MINOR_REVISIONS"], + "Minor Revisions Required", + ), + ( + review.const.EditorialDecisions["MAJOR_REVISIONS"], + "Major Revisions Required", + ), + (review.const.EditorialDecisions["DECLINE"], "Reject"), + ], + max_length=100, + verbose_name="Draft Decision", + ), ), migrations.AlterField( - model_name='decisiondraft', - name='editor_decision', - field=models.CharField(blank=True, choices=[(review.const.EditorialDecisions['ACCEPT'], 'Accept'), (review.const.EditorialDecisions['DECLINE'], 'Decline')], max_length=20, null=True), + model_name="decisiondraft", + name="editor_decision", + field=models.CharField( + blank=True, + choices=[ + (review.const.EditorialDecisions["ACCEPT"], "Accept"), + (review.const.EditorialDecisions["DECLINE"], "Decline"), + ], + max_length=20, + null=True, + ), ), migrations.AlterField( - model_name='reviewassignment', - name='decision', - field=models.CharField(blank=True, choices=[(review.const.EditorialDecisions['ACCEPT'], 'Accept Without Revisions'), (review.const.EditorialDecisions['MINOR_REVISIONS'], 'Minor Revisions Required'), (review.const.EditorialDecisions['MAJOR_REVISIONS'], 'Major Revisions Required'), (review.const.EditorialDecisions['DECLINE'], 'Reject')], max_length=20, null=True, verbose_name='Recommendation'), + model_name="reviewassignment", + name="decision", + field=models.CharField( + blank=True, + choices=[ + ( + review.const.EditorialDecisions["ACCEPT"], + "Accept Without Revisions", + ), + ( + review.const.EditorialDecisions["MINOR_REVISIONS"], + "Minor Revisions Required", + ), + ( + review.const.EditorialDecisions["MAJOR_REVISIONS"], + "Major Revisions Required", + ), + (review.const.EditorialDecisions["DECLINE"], "Reject"), + ], + max_length=20, + null=True, + verbose_name="Recommendation", + ), ), migrations.AlterField( - model_name='revisionrequest', - name='type', - field=models.CharField(choices=[(review.const.EditorialDecisions['MINOR_REVISIONS'], 'Minor Revisions'), (review.const.EditorialDecisions['MAJOR_REVISIONS'], 'Major Revisions')], default='minor_revisions', max_length=20), + model_name="revisionrequest", + name="type", + field=models.CharField( + choices=[ + ( + review.const.EditorialDecisions["MINOR_REVISIONS"], + "Minor Revisions", + ), + ( + review.const.EditorialDecisions["MAJOR_REVISIONS"], + "Major Revisions", + ), + ], + default="minor_revisions", + max_length=20, + ), ), migrations.RunPython(reject_to_decline, reverse_code=decline_to_reject), ] diff --git a/src/review/migrations/0020_merge_20230510_1129.py b/src/review/migrations/0020_merge_20230510_1129.py index 971baa61aa..ed3ace619d 100644 --- a/src/review/migrations/0020_merge_20230510_1129.py +++ b/src/review/migrations/0020_merge_20230510_1129.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0019_auto_20230418_1113'), - ('review', '0019_decision_decline_rewording'), + ("review", "0019_auto_20230418_1113"), + ("review", "0019_decision_decline_rewording"), ] - operations = [ - ] + operations = [] diff --git a/src/review/migrations/0021_auto_20230530_1442.py b/src/review/migrations/0021_auto_20230530_1442.py index 4d6c97c69a..cb130224a5 100644 --- a/src/review/migrations/0021_auto_20230530_1442.py +++ b/src/review/migrations/0021_auto_20230530_1442.py @@ -4,23 +4,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0020_merge_20230510_1129'), + ("review", "0020_merge_20230510_1129"), ] operations = [ migrations.AddField( - model_name='revisionrequest', - name='response_letter', - field=models.TextField(blank=True, help_text='You have the option to include a response letter for the reviewers, providing details about the changes you made to your manuscript or counter arguments.', null=True, verbose_name='Response Letter to Reviewers'), + model_name="revisionrequest", + name="response_letter", + field=models.TextField( + blank=True, + help_text="You have the option to include a response letter for the reviewers, providing details about the changes you made to your manuscript or counter arguments.", + null=True, + verbose_name="Response Letter to Reviewers", + ), ), migrations.AlterField( - model_name='revisionrequest', - name='author_note', - field=models.TextField(blank=True, - help_text="If you would like to include a cover letter for the editor providing changes you made to your revised manuscript, please add this above'", - null=True, - verbose_name='Covering Letter to Editor'), + model_name="revisionrequest", + name="author_note", + field=models.TextField( + blank=True, + help_text="If you would like to include a cover letter for the editor providing changes you made to your revised manuscript, please add this above'", + null=True, + verbose_name="Covering Letter to Editor", + ), ), ] diff --git a/src/review/migrations/0022_remove_reviewform_slug.py b/src/review/migrations/0022_remove_reviewform_slug.py index 5175c6ea37..b84f48f785 100644 --- a/src/review/migrations/0022_remove_reviewform_slug.py +++ b/src/review/migrations/0022_remove_reviewform_slug.py @@ -4,14 +4,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0021_auto_20230530_1442'), + ("review", "0021_auto_20230530_1442"), ] operations = [ migrations.RemoveField( - model_name='reviewform', - name='slug', + model_name="reviewform", + name="slug", ), ] diff --git a/src/review/migrations/0023_auto_20240312_0922.py b/src/review/migrations/0023_auto_20240312_0922.py index f243407868..ebb7a18d01 100644 --- a/src/review/migrations/0023_auto_20240312_0922.py +++ b/src/review/migrations/0023_auto_20240312_0922.py @@ -5,105 +5,167 @@ class Migration(migrations.Migration): - dependencies = [ - ('review', '0022_remove_reviewform_slug'), + ("review", "0022_remove_reviewform_slug"), ] operations = [ migrations.AlterField( - model_name='decisiondraft', - name='decision', - field=models.CharField(choices=[('accept', 'Accept Without Revisions'), ('minor_revisions', 'Minor Revisions Required'), ('major_revisions', 'Major Revisions Required'), ('decline', 'Reject')], max_length=100, verbose_name='Draft Decision'), - ), - migrations.AlterField( - model_name='decisiondraft', - name='editor_decision', - field=models.CharField(blank=True, choices=[('accept', 'Accept'), ('decline', 'Decline')], max_length=20, null=True), - ), - migrations.AlterField( - model_name='decisiondraft', - name='editor_decline_rationale', - field=core.model_utils.JanewayBleachField(blank=True, help_text='Provide the section editor with a rationale for declining their drafted decision.', null=True, verbose_name='Rationale for Declining Draft Decision'), - ), - migrations.AlterField( - model_name='decisiondraft', - name='email_message', - field=core.model_utils.JanewayBleachField(blank=True, help_text='This is a draft of the email that will be sent to the author. Your editor will check this.', null=True, verbose_name='Draft Email to Author'), - ), - migrations.AlterField( - model_name='decisiondraft', - name='message_to_editor', - field=core.model_utils.JanewayBleachField(blank=True, help_text='This is the email that will be sent to the editor notifying them that you are logging your draft decision.', null=True, verbose_name='Email to Editor'), - ), - migrations.AlterField( - model_name='frozenreviewformelement', - name='help_text', + model_name="decisiondraft", + name="decision", + field=models.CharField( + choices=[ + ("accept", "Accept Without Revisions"), + ("minor_revisions", "Minor Revisions Required"), + ("major_revisions", "Major Revisions Required"), + ("decline", "Reject"), + ], + max_length=100, + verbose_name="Draft Decision", + ), + ), + migrations.AlterField( + model_name="decisiondraft", + name="editor_decision", + field=models.CharField( + blank=True, + choices=[("accept", "Accept"), ("decline", "Decline")], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="decisiondraft", + name="editor_decline_rationale", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Provide the section editor with a rationale for declining their drafted decision.", + null=True, + verbose_name="Rationale for Declining Draft Decision", + ), + ), + migrations.AlterField( + model_name="decisiondraft", + name="email_message", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="This is a draft of the email that will be sent to the author. Your editor will check this.", + null=True, + verbose_name="Draft Email to Author", + ), + ), + migrations.AlterField( + model_name="decisiondraft", + name="message_to_editor", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="This is the email that will be sent to the editor notifying them that you are logging your draft decision.", + null=True, + verbose_name="Email to Editor", + ), + ), + migrations.AlterField( + model_name="frozenreviewformelement", + name="help_text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='reviewassignment', - name='comments_for_editor', - field=core.model_utils.JanewayBleachField(blank=True, help_text='If you have any comments for the Editor you can add them here; these will not be shared with the Author.', null=True, verbose_name='Comments for the Editor'), + model_name="reviewassignment", + name="comments_for_editor", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If you have any comments for the Editor you can add them here; these will not be shared with the Author.", + null=True, + verbose_name="Comments for the Editor", + ), ), migrations.AlterField( - model_name='reviewassignment', - name='competing_interests', - field=core.model_utils.JanewayBleachField(blank=True, help_text="If any of the authors or editors have any competing interests please add them here. EG. 'This study was paid for by corp xyz.'.", null=True), + model_name="reviewassignment", + name="competing_interests", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If any of the authors or editors have any competing interests please add them here. EG. 'This study was paid for by corp xyz.'.", + null=True, + ), ), migrations.AlterField( - model_name='reviewassignmentanswer', - name='answer', + model_name="reviewassignmentanswer", + name="answer", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='reviewassignmentanswer', - name='edited_answer', + model_name="reviewassignmentanswer", + name="edited_answer", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='reviewform', - name='intro', - field=core.model_utils.JanewayBleachField(help_text='Message displayed at the start of the review form.'), + model_name="reviewform", + name="intro", + field=core.model_utils.JanewayBleachField( + help_text="Message displayed at the start of the review form." + ), ), migrations.AlterField( - model_name='reviewform', - name='thanks', - field=core.model_utils.JanewayBleachField(help_text='Message displayed after the reviewer is finished.'), + model_name="reviewform", + name="thanks", + field=core.model_utils.JanewayBleachField( + help_text="Message displayed after the reviewer is finished." + ), ), migrations.AlterField( - model_name='reviewformanswer', - name='answer', + model_name="reviewformanswer", + name="answer", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='reviewformelement', - name='help_text', + model_name="reviewformelement", + name="help_text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='revisionaction', - name='text', + model_name="revisionaction", + name="text", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='revisionrequest', - name='author_note', - field=core.model_utils.JanewayBleachField(blank=True, help_text="If you would like to include a cover letter for the editor providing changes you made to your revised manuscript, please add this above'", null=True, verbose_name='Covering Letter to Editor'), - ), - migrations.AlterField( - model_name='revisionrequest', - name='editor_note', - field=core.model_utils.JanewayBleachField(blank=True, help_text='You can use this optional field to provide the author with any information that may help them when evaluating the article reviews and integrating changes into their manuscript. This text will be displayed to the author on the revision page, above the reviews.', null=True), - ), - migrations.AlterField( - model_name='revisionrequest', - name='response_letter', - field=core.model_utils.JanewayBleachField(blank=True, help_text='You have the option to include a response letter for the reviewers, providing details about the changes you made to your manuscript or counter arguments.', null=True, verbose_name='Response Letter to Reviewers'), - ), - migrations.AlterField( - model_name='revisionrequest', - name='type', - field=models.CharField(choices=[('minor_revisions', 'Minor Revisions'), ('major_revisions', 'Major Revisions')], default='minor_revisions', max_length=20), + model_name="revisionrequest", + name="author_note", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If you would like to include a cover letter for the editor providing changes you made to your revised manuscript, please add this above'", + null=True, + verbose_name="Covering Letter to Editor", + ), + ), + migrations.AlterField( + model_name="revisionrequest", + name="editor_note", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="You can use this optional field to provide the author with any information that may help them when evaluating the article reviews and integrating changes into their manuscript. This text will be displayed to the author on the revision page, above the reviews.", + null=True, + ), + ), + migrations.AlterField( + model_name="revisionrequest", + name="response_letter", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="You have the option to include a response letter for the reviewers, providing details about the changes you made to your manuscript or counter arguments.", + null=True, + verbose_name="Response Letter to Reviewers", + ), + ), + migrations.AlterField( + model_name="revisionrequest", + name="type", + field=models.CharField( + choices=[ + ("minor_revisions", "Minor Revisions"), + ("major_revisions", "Major Revisions"), + ], + default="minor_revisions", + max_length=20, + ), ), ] diff --git a/src/review/models.py b/src/review/models.py index 397102c141..f0a4d648b5 100755 --- a/src/review/models.py +++ b/src/review/models.py @@ -15,14 +15,14 @@ from review.const import ( EditorialDecisions as ED, ReviewerDecisions as RD, - VisibilityOptions as VO + VisibilityOptions as VO, ) from utils import shared assignment_choices = ( - ('editor', 'Editor'), - ('section-editor', 'Section Editor'), + ("editor", "Editor"), + ("section-editor", "Section Editor"), ) @@ -31,11 +31,11 @@ def all_review_decisions(): Review decision options presented in admin. """ return ( - (RD.DECISION_ACCEPT.value, 'Accept Without Revisions'), - (RD.DECISION_MINOR.value, 'Minor Revisions Required'), - (RD.DECISION_MAJOR.value, 'Major Revisions Required'), - (RD.DECISION_REJECT.value, 'Reject'), - (RD.DECISION_NO_RECOMMENDATION.value, 'No Recommendation'), + (RD.DECISION_ACCEPT.value, "Accept Without Revisions"), + (RD.DECISION_MINOR.value, "Minor Revisions Required"), + (RD.DECISION_MAJOR.value, "Major Revisions Required"), + (RD.DECISION_REJECT.value, "Reject"), + (RD.DECISION_NO_RECOMMENDATION.value, "No Recommendation"), ) @@ -44,47 +44,47 @@ def reviewer_decision_choices(): Review decision options presented to a Reviewer. """ return ( - (None, '-----------'), - (RD.DECISION_ACCEPT.value, 'Accept Without Revisions'), - (RD.DECISION_MINOR.value, 'Minor Revisions Required'), - (RD.DECISION_MAJOR.value, 'Major Revisions Required'), - (RD.DECISION_REJECT.value, 'Reject'), + (None, "-----------"), + (RD.DECISION_ACCEPT.value, "Accept Without Revisions"), + (RD.DECISION_MINOR.value, "Minor Revisions Required"), + (RD.DECISION_MAJOR.value, "Major Revisions Required"), + (RD.DECISION_REJECT.value, "Reject"), ) def review_decision(): return ( - (ED.ACCEPT.value, 'Accept Without Revisions'), - (ED.MINOR_REVISIONS.value, 'Minor Revisions Required'), - (ED.MAJOR_REVISIONS.value, 'Major Revisions Required'), + (ED.ACCEPT.value, "Accept Without Revisions"), + (ED.MINOR_REVISIONS.value, "Minor Revisions Required"), + (ED.MAJOR_REVISIONS.value, "Major Revisions Required"), # Preserved the inconsistent verbose name below to avoid confusion to # existing section editors - (ED.DECLINE.value, 'Reject'), + (ED.DECLINE.value, "Reject"), ) def review_type(): return ( - ('traditional', 'Traditional'), + ("traditional", "Traditional"), # ('annotation', 'Annotation'), ) def review_visibilty(): return ( - (VO.OPEN.value, 'Open'), - (VO.SINGLE_ANON.value, 'Single Anonymous'), - (VO.DOUBLE_ANON.value, 'Double Anonymous') + (VO.OPEN.value, "Open"), + (VO.SINGLE_ANON.value, "Single Anonymous"), + (VO.DOUBLE_ANON.value, "Double Anonymous"), ) class EditorAssignment(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) editor = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) editor_type = models.CharField(max_length=20, choices=assignment_choices) @@ -92,45 +92,53 @@ class EditorAssignment(models.Model): notified = models.BooleanField(default=False) class Meta: - unique_together = ('article', 'editor') + unique_together = ("article", "editor") class ReviewRound(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) round_number = models.IntegerField() - review_files = models.ManyToManyField('core.File') + review_files = models.ManyToManyField("core.File") date_started = models.DateTimeField(auto_now_add=True) class Meta: - unique_together = ('article', 'round_number') - ordering = ('-round_number',) + unique_together = ("article", "round_number") + ordering = ("-round_number",) def __str__(self): - return u'%s - %s round_number: %s' % (self.pk, self.article.title, self.round_number) + return "%s - %s round_number: %s" % ( + self.pk, + self.article.title, + self.round_number, + ) def __repr__(self): - return u'%s - %s round number: %s' % (self.pk, self.article.title, self.round_number) + return "%s - %s round number: %s" % ( + self.pk, + self.article.title, + self.round_number, + ) def active_reviews(self): return self.reviewassignment_set.exclude( - Q(date_declined__isnull=False) | Q(decision='withdrawn') + Q(date_declined__isnull=False) | Q(decision="withdrawn") ).order_by( - '-decision', + "-decision", ) def inactive_reviews(self): return self.reviewassignment_set.filter( - Q(date_declined__isnull=False) | Q(decision='withdrawn') + Q(date_declined__isnull=False) | Q(decision="withdrawn") ).order_by( - 'decision', + "decision", ) @classmethod def latest_article_round(cls, article): - """ Works out and returns the latest article review round + """Works out and returns the latest article review round MS: I'm still not quite sure why it works but it does the round with a single query: SELECT "review_reviewround"."*" @@ -142,11 +150,13 @@ def latest_article_round(cls, article): ) ORDER BY "review_reviewround"."round_number" DESC """ - latest_round = cls.objects.filter( - article=article - ).aggregate( - latest_round_number=Max('round_number'), - ).get('latest_round_number', 0) + latest_round = ( + cls.objects.filter(article=article) + .aggregate( + latest_round_number=Max("round_number"), + ) + .get("latest_round_number", 0) + ) return cls.objects.get(article=article, round_number=latest_round) @@ -154,20 +164,20 @@ def latest_article_round(cls, article): class ReviewAssignment(models.Model): # FKs article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) reviewer = models.ForeignKey( - 'core.Account', - related_name='reviewer', - help_text='User to undertake the review', + "core.Account", + related_name="reviewer", + help_text="User to undertake the review", null=True, on_delete=models.SET_NULL, ) editor = models.ForeignKey( - 'core.Account', - related_name='editor', - help_text='Editor requesting the review', + "core.Account", + related_name="editor", + help_text="Editor requesting the review", null=True, on_delete=models.SET_NULL, ) @@ -184,20 +194,28 @@ class ReviewAssignment(models.Model): blank=True, null=True, choices=all_review_decisions(), - verbose_name='Recommendation', - ) - competing_interests = model_utils.JanewayBleachField(blank=True, null=True, - help_text="If any of the authors or editors " - "have any competing interests please add them here. " - "EG. 'This study was paid for by corp xyz.'.") - review_type = models.CharField(max_length=20, choices=review_type(), default='traditional', - help_text='Currently only traditional, form based, review is available.') + verbose_name="Recommendation", + ) + competing_interests = model_utils.JanewayBleachField( + blank=True, + null=True, + help_text="If any of the authors or editors " + "have any competing interests please add them here. " + "EG. 'This study was paid for by corp xyz.'.", + ) + review_type = models.CharField( + max_length=20, + choices=review_type(), + default="traditional", + help_text="Currently only traditional, form based, review is available.", + ) visibility = models.CharField( - max_length=20, choices=review_visibilty(), - default='double-blind', + max_length=20, + choices=review_visibilty(), + default="double-blind", verbose_name=_("Anonymity"), ) - form = models.ForeignKey('ReviewForm', null=True, on_delete=models.SET_NULL) + form = models.ForeignKey("ReviewForm", null=True, on_delete=models.SET_NULL) access_code = models.CharField(max_length=100, blank=True, null=True) # Dates @@ -212,27 +230,38 @@ class ReviewAssignment(models.Model): for_author_consumption = models.BooleanField(default=False) suggested_reviewers = models.TextField(blank=True, null=True) - comments_for_editor = model_utils.JanewayBleachField(blank=True, null=True, - help_text="If you have any comments for the Editor you can add them here; \ + comments_for_editor = model_utils.JanewayBleachField( + blank=True, + null=True, + help_text="If you have any comments for the Editor you can add them here; \ these will not be shared with the Author.", - verbose_name="Comments for the Editor") + verbose_name="Comments for the Editor", + ) review_file = models.ForeignKey( - 'core.File', + "core.File", blank=True, null=True, on_delete=models.SET_NULL, ) display_review_file = models.BooleanField(default=False) - permission_to_make_public = models.BooleanField(default=False, - help_text='This journal has a policy of sharing reviews openly alongside the published article to aid in transparency. If you give permission here and the article is published, your name and review will be visible.') - display_public = models.BooleanField(default=False, help_text='Whether this review should be publicly displayed.') + permission_to_make_public = models.BooleanField( + default=False, + help_text="This journal has a policy of sharing reviews openly alongside the published article to aid in transparency. If you give permission here and the article is published, your name and review will be visible.", + ) + display_public = models.BooleanField( + default=False, help_text="Whether this review should be publicly displayed." + ) def review_form_answers(self): - return ReviewAssignmentAnswer.objects.filter(assignment=self).order_by('frozen_element__order') + return ReviewAssignmentAnswer.objects.filter(assignment=self).order_by( + "frozen_element__order" + ) def save_review_form(self, review_form, assignment): for k, v in review_form.cleaned_data.items(): - form_element = ReviewFormElement.objects.get(reviewform=assignment.form, pk=k) + form_element = ReviewFormElement.objects.get( + reviewform=assignment.form, pk=k + ) answer, _ = ReviewAssignmentAnswer.objects.update_or_create( assignment=self, original_element=form_element, @@ -266,10 +295,14 @@ def task_is_late(self): # imports are here to avoid circular dependency from core import models as core_models from utils import workflow_tasks + active_tasks = core_models.Task.objects.filter( - Q(content_type=ContentType.objects.get_for_model(self.article)) & Q(object_id=self.article.pk) & Q( - completed__isnull=True) & Q(title=workflow_tasks.DO_REVIEW_TITLE) & Q( - assignees=self.reviewer)) + Q(content_type=ContentType.objects.get_for_model(self.article)) + & Q(object_id=self.article.pk) + & Q(completed__isnull=True) + & Q(title=workflow_tasks.DO_REVIEW_TITLE) + & Q(assignees=self.reviewer) + ) for task in active_tasks: if task.is_late: @@ -281,53 +314,53 @@ def task_is_late(self): def status(self): if self.decision == RD.DECISION_WITHDRAWN.value: return { - 'code': 'withdrawn', - 'display': 'Withdrawn', - 'span_class': 'red', - 'date': '', - 'reminder': None, + "code": "withdrawn", + "display": "Withdrawn", + "span_class": "red", + "date": "", + "reminder": None, } elif self.date_complete: return { - 'code': 'complete', - 'display': 'Complete', - 'span_class': 'light-green', - 'date': shared.day_month(self.date_complete), - 'reminder': None, + "code": "complete", + "display": "Complete", + "span_class": "light-green", + "date": shared.day_month(self.date_complete), + "reminder": None, } elif self.date_accepted: return { - 'code': 'accept', - 'display': 'Yes', - 'span_class': 'green', - 'date': shared.day_month(self.date_accepted), - 'reminder': 'accepted', + "code": "accept", + "display": "Yes", + "span_class": "green", + "date": shared.day_month(self.date_accepted), + "reminder": "accepted", } elif self.date_declined: return { - 'code': 'declined', - 'display': 'No', - 'span_class': 'red', - 'date': shared.day_month(self.date_declined), - 'reminder': None, + "code": "declined", + "display": "No", + "span_class": "red", + "date": shared.day_month(self.date_declined), + "reminder": None, } else: return { - 'code': 'wait', - 'display': 'Wait', - 'span_class': 'amber', - 'date': '', - 'reminder': 'request', + "code": "wait", + "display": "Wait", + "span_class": "amber", + "date": "", + "reminder": "request", } def request_decision_status(self): if self.decision == RD.DECISION_WITHDRAWN.value: - return f'Withdrawn {self.date_complete.date()}' + return f"Withdrawn {self.date_complete.date()}" elif self.date_accepted: - return f'Accepted {self.date_accepted.date()}' + return f"Accepted {self.date_accepted.date()}" elif self.date_declined: - return f'Declined {self.date_declined.date()}' - return 'Awaiting acknowledgement' + return f"Declined {self.date_declined.date()}" + return "Awaiting acknowledgement" def visibility_statement(self): if self.for_author_consumption: @@ -340,13 +373,14 @@ def __str__(self): else: reviewer_name = "No reviewer" - return u'{0} - Article: {1}, Reviewer: {2}'.format( - self.id, self.article.title, reviewer_name) + return "{0} - Article: {1}, Reviewer: {2}".format( + self.id, self.article.title, reviewer_name + ) class ReviewForm(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) @@ -359,7 +393,7 @@ class ReviewForm(models.Model): help_text="Message displayed after the reviewer is finished.", ) - elements = models.ManyToManyField('ReviewFormElement') + elements = models.ManyToManyField("ReviewFormElement") deleted = models.BooleanField(default=False) def __str__(self): @@ -368,40 +402,47 @@ def __str__(self): def element_kind_choices(): return ( - ('text', 'Text Field'), - ('textarea', 'Text Area'), - ('check', 'Check Box'), - ('select', 'Select'), - ('email', 'Email'), - ('upload', 'Upload'), - ('date', 'Date'), + ("text", "Text Field"), + ("textarea", "Text Area"), + ("check", "Check Box"), + ("select", "Select"), + ("email", "Email"), + ("upload", "Upload"), + ("date", "Date"), ) def element_width_choices(): return ( - ('large-4 columns', 'third'), - ('large-6 columns', 'half'), - ('large-12 columns', 'full'), + ("large-4 columns", "third"), + ("large-6 columns", "half"), + ("large-12 columns", "full"), ) class BaseReviewFormElement(models.Model): name = models.CharField(max_length=200) kind = models.CharField(max_length=50, choices=element_kind_choices()) - choices = models.CharField(max_length=1000, null=True, blank=True, - help_text='Seperate choices with the bar | character.') + choices = models.CharField( + max_length=1000, + null=True, + blank=True, + help_text="Seperate choices with the bar | character.", + ) required = models.BooleanField(default=True) order = models.IntegerField() width = models.CharField(max_length=20, choices=element_width_choices()) help_text = model_utils.JanewayBleachField(blank=True, null=True) - default_visibility = models.BooleanField(default=True, help_text='If true, this setting will be available ' - 'to the author automatically, if false it will' - 'be hidden to the author by default.') + default_visibility = models.BooleanField( + default=True, + help_text="If true, this setting will be available " + "to the author automatically, if false it will" + "be hidden to the author by default.", + ) class Meta: - ordering = ('order', 'name') + ordering = ("order", "name") abstract = True def __str__(self): @@ -429,7 +470,7 @@ def snapshot(self, answer): width=self.width, help_text=self.help_text, default_visibility=self.default_visibility, - ) + ), ) return frozen @@ -462,11 +503,14 @@ def best_label(self): elif self.frozen_element: return self.frozen_element.name else: - return 'element' # this is a fallback incase the two links above are removed. + return ( + "element" # this is a fallback incase the two links above are removed. + ) class FrozenReviewFormElement(BaseReviewFormElement): - """ A snapshot of a review form element at the time an answer is created""" + """A snapshot of a review form element at the time an answer is created""" + form_element = models.ForeignKey( ReviewFormElement, null=True, @@ -499,17 +543,20 @@ class ReviewerRating(models.Model): ReviewAssignment, on_delete=models.CASCADE, ) - rating = models.IntegerField(validators=[MinValueValidator(1), - MaxValueValidator(10)]) + rating = models.IntegerField( + validators=[MinValueValidator(1), MaxValueValidator(10)] + ) rater = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) def __str__(self): return "Reviewer: {0}, Article: {1}, Rating: {2}".format( - self.assignment.reviewer.full_name(), self.assignment.article.title, self.rating + self.assignment.reviewer.full_name(), + self.assignment.article.title, + self.rating, ) @@ -517,7 +564,7 @@ class RevisionAction(models.Model): text = model_utils.JanewayBleachField() logged = models.DateTimeField(default=None, null=True, blank=True) user = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -528,18 +575,18 @@ def __str__(self): def revision_type(): return ( - (ED.MINOR_REVISIONS.value, 'Minor Revisions'), - (ED.MAJOR_REVISIONS.value, 'Major Revisions'), + (ED.MINOR_REVISIONS.value, "Minor Revisions"), + (ED.MAJOR_REVISIONS.value, "Major Revisions"), ) class RevisionRequest(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) editor = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -547,33 +594,35 @@ class RevisionRequest(models.Model): blank=True, null=True, help_text="You can use this optional field to provide the author with " - "any information that may help them when evaluating the " - "article reviews and integrating changes into their " - "manuscript. This text will be displayed to the author on " - "the revision page, above the reviews.", + "any information that may help them when evaluating the " + "article reviews and integrating changes into their " + "manuscript. This text will be displayed to the author on " + "the revision page, above the reviews.", ) author_note = model_utils.JanewayBleachField( blank=True, null=True, verbose_name="Covering Letter to Editor", help_text="If you would like to include a cover letter for the editor " - "providing changes you made to your revised manuscript, " - "please add this above'" + "providing changes you made to your revised manuscript, " + "please add this above'", ) # Note from Author to Editor - actions = models.ManyToManyField(RevisionAction) # List of actions Author took during Revision Request + actions = models.ManyToManyField( + RevisionAction + ) # List of actions Author took during Revision Request type = models.CharField( max_length=20, choices=revision_type(), - default='minor_revisions', + default="minor_revisions", ) response_letter = model_utils.JanewayBleachField( blank=True, null=True, verbose_name="Response Letter to Reviewers", - help_text='You have the option to include a response letter for the ' - 'reviewers, providing details about the changes you made ' - 'to your manuscript or counter arguments.', + help_text="You have the option to include a response letter for the " + "reviewers, providing details about the changes you made " + "to your manuscript or counter arguments.", ) date_requested = models.DateTimeField(default=timezone.now) @@ -581,64 +630,68 @@ class RevisionRequest(models.Model): date_completed = models.DateTimeField(blank=True, null=True) def __str__(self): - return "Revision of {0} requested by {1}".format(self.article.title, self.editor.full_name()) + return "Revision of {0} requested by {1}".format( + self.article.title, self.editor.full_name() + ) class EditorOverride(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) editor = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) overwritten = models.DateTimeField(default=timezone.now) def __str__(self): - return "{0} overrode their access to {1}".format(self.editor.full_name(), self.article.title) + return "{0} overrode their access to {1}".format( + self.editor.full_name(), self.article.title + ) class DecisionDraft(models.Model): article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, ) editor = models.ForeignKey( - 'core.Account', - related_name='draft_editor', + "core.Account", + related_name="draft_editor", null=True, on_delete=models.SET_NULL, ) section_editor = models.ForeignKey( - 'core.Account', - related_name='draft_section_editor', + "core.Account", + related_name="draft_section_editor", null=True, on_delete=models.SET_NULL, ) decision = models.CharField( max_length=100, choices=review_decision(), - verbose_name='Draft Decision', + verbose_name="Draft Decision", ) message_to_editor = model_utils.JanewayBleachField( null=True, blank=True, - help_text='This is the email that will be sent to the editor notifying them that you are ' - 'logging your draft decision.', - verbose_name='Email to Editor', + help_text="This is the email that will be sent to the editor notifying them that you are " + "logging your draft decision.", + verbose_name="Email to Editor", ) email_message = model_utils.JanewayBleachField( null=True, blank=True, - help_text='This is a draft of the email that will be sent to the author. Your editor will check this.', - verbose_name='Draft Email to Author', + help_text="This is a draft of the email that will be sent to the author. Your editor will check this.", + verbose_name="Draft Email to Author", ) drafted = models.DateTimeField(auto_now=True) editor_decision = models.CharField( max_length=20, - choices=((ED.ACCEPT.value, 'Accept'), (ED.DECLINE.value, 'Decline')), + choices=((ED.ACCEPT.value, "Accept"), (ED.DECLINE.value, "Decline")), null=True, blank=True, ) @@ -655,5 +708,4 @@ class DecisionDraft(models.Model): ) def __str__(self): - return "{0}: {1}".format(self.article.title, - self.decision) + return "{0}: {1}".format(self.article.title, self.decision) diff --git a/src/review/tests.py b/src/review/tests.py index a7ae5cf7c4..850a9240b7 100644 --- a/src/review/tests.py +++ b/src/review/tests.py @@ -29,22 +29,22 @@ class ReviewTests(TestCase): def setUp(self): self.files = list() - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_index_view_with_no_questions(self): """ If no questions exist, an appropriate message should be displayed. """ - response = self.client.get('/') + response = self.client.get("/") self.assertEqual(response.status_code, 200) def test_review_form_can_save(self): - review_field_text = 'Here is a review of this paper.' + review_field_text = "Here is a review of this paper." first_form = forms.GeneratedForm( review_assignment=self.review_assignment, fields_required=False, data={ str(self.review_form_element.pk): review_field_text, - } + }, ) first_form.is_valid() self.review_assignment.save_review_form(first_form, self.review_assignment) @@ -71,32 +71,36 @@ def test_completed_reviews_with_decision_count(self): def test_review_assignment_form_valid(self): data = { - 'visibility': 'double-blind', - 'form': self.review_form.pk, - 'date_due': '2900-01-01', - 'reviewer': self.second_reviewer.pk, + "visibility": "double-blind", + "form": self.review_form.pk, + "date_due": "2900-01-01", + "reviewer": self.second_reviewer.pk, } form = forms.ReviewAssignmentForm( journal=self.journal_one, article=self.article_under_review, editor=self.editor, - reviewers=logic.get_reviewer_candidates(self.article_under_review, self.editor), + reviewers=logic.get_reviewer_candidates( + self.article_under_review, self.editor + ), data=data, ) self.assertTrue(form.is_valid()) def test_review_assignment_form_bad_reviewer(self): data = { - 'visibility': 'double-blind', - 'form': self.review_form.pk, - 'date_due': '2900-01-01', - 'reviewer': self.regular_user.pk, + "visibility": "double-blind", + "form": self.review_form.pk, + "date_due": "2900-01-01", + "reviewer": self.regular_user.pk, } form = forms.ReviewAssignmentForm( journal=self.journal_one, article=self.article_under_review, editor=self.editor, - reviewers=logic.get_reviewer_candidates(self.article_under_review, self.editor), + reviewers=logic.get_reviewer_candidates( + self.article_under_review, self.editor + ), data=data, ) self.assertFalse(form.is_valid()) @@ -104,9 +108,11 @@ def test_review_assignment_form_bad_reviewer(self): def test_reviewer_decision_form_no_decision(self): article = helpers.create_article( self.journal_one, - **{'owner': self.regular_user, - 'title': 'Test Article', - 'stage': submission_models.STAGE_UNDER_REVIEW}, + **{ + "owner": self.regular_user, + "title": "Test Article", + "stage": submission_models.STAGE_UNDER_REVIEW, + }, ) round, c = review_models.ReviewRound.objects.get_or_create( article=article, @@ -114,10 +120,10 @@ def test_reviewer_decision_form_no_decision(self): ) data = { - 'reviewer': str(self.regular_user.id), - 'form': self.review_form.pk, - 'visibility': 'double-blind', - 'date_due': '2024-03-13', + "reviewer": str(self.regular_user.id), + "form": self.review_form.pk, + "visibility": "double-blind", + "date_due": "2024-03-13", } form = forms.ReviewAssignmentForm( data, @@ -132,10 +138,7 @@ def test_reviewer_decision_form_no_decision(self): decision_required=True, ) - self.assertIn( - "", - form.as_p() - ) + self.assertIn('', form.as_p()) self.assertFalse(form.is_valid()) def test_csv_reviewer_import_good(self): @@ -148,17 +151,16 @@ def test_csv_reviewer_import_good(self): self.good_reviewer_content_line, ) self.test_csv = SimpleUploadedFile( - 'test_reviewer_file.csv', - bytes(csv_content.encode("utf-8")) + "test_reviewer_file.csv", bytes(csv_content.encode("utf-8")) ) filename, path = files.save_file_to_temp(self.test_csv) # prep form with data details_form = forms.BulkReviewAssignmentForm( { - 'visibility': 'double-blind', - 'form': self.review_form, - 'date_due': '2023-01-01', + "visibility": "double-blind", + "form": self.review_form, + "date_due": "2023-01-01", } ) details_form.is_valid() @@ -171,30 +173,27 @@ def test_csv_reviewer_import_good(self): ) self.assertTrue( isinstance( - reviewers[0].get('review_assignment'), + reviewers[0].get("review_assignment"), review_models.ReviewAssignment, ) ) - self.assertFalse( - error - ) + self.assertFalse(error) os.unlink(path) def test_csv_reviewer_import_bad(self): # create a fake request object request = self.setup_request_object() self.test_csv = SimpleUploadedFile( - 'test_reviewer_file.csv', - bytes(self.empty_reviewer_content_line) + "test_reviewer_file.csv", bytes(self.empty_reviewer_content_line) ) filename, path = files.save_file_to_temp(self.test_csv) # prep form with data details_form = forms.BulkReviewAssignmentForm( { - 'visibility': 'double-blind', - 'form': self.review_form, - 'date_due': '2023-01-01', + "visibility": "double-blind", + "form": self.review_form, + "date_due": "2023-01-01", } ) details_form.is_valid() @@ -216,17 +215,16 @@ def test_csv_review_import_uses_existing_user_account(self): self.regular_user_csv_line, ) self.test_csv = SimpleUploadedFile( - 'test_reviewer_file.csv', - bytes(csv_content.encode("utf-8")) + "test_reviewer_file.csv", bytes(csv_content.encode("utf-8")) ) filename, path = files.save_file_to_temp(self.test_csv) # prep form with data details_form = forms.BulkReviewAssignmentForm( { - 'visibility': 'double-blind', - 'form': self.review_form, - 'date_due': '2023-01-01', + "visibility": "double-blind", + "form": self.review_form, + "date_due": "2023-01-01", } ) details_form.is_valid() @@ -237,7 +235,7 @@ def test_csv_review_import_uses_existing_user_account(self): details_form, ) self.assertTrue( - reviewers[0].get('account'), + reviewers[0].get("account"), self.regular_user, ) @@ -250,17 +248,16 @@ def test_csv_doesnt_create_duplicate_assignments(self): self.regular_user_csv_line, ) self.test_csv = SimpleUploadedFile( - 'test_reviewer_file.csv', - bytes(csv_content.encode("utf-8")) + "test_reviewer_file.csv", bytes(csv_content.encode("utf-8")) ) filename, path = files.save_file_to_temp(self.test_csv) # prep form with data details_form = forms.BulkReviewAssignmentForm( { - 'visibility': 'double-blind', - 'form': self.review_form, - 'date_due': '2023-01-01', + "visibility": "double-blind", + "form": self.review_form, + "date_due": "2023-01-01", } ) details_form.is_valid() @@ -271,50 +268,60 @@ def test_csv_doesnt_create_duplicate_assignments(self): details_form, ) self.assertTrue( - reviewers[0].get('review_assignment'), + reviewers[0].get("review_assignment"), self.review_assignment_complete, ) def test_incomplete_reviews(self): - args = {'owner': self.regular_user, - 'title': 'Test Article', - 'stage': submission_models.STAGE_UNDER_REVIEW,} + args = { + "owner": self.regular_user, + "title": "Test Article", + "stage": submission_models.STAGE_UNDER_REVIEW, + } article1 = helpers.create_article(self.journal_one, **args) article1.correspondence_author = self.regular_user article1.save() - round = review_models.ReviewRound.objects.create(article=article1, - round_number=1,) + round = review_models.ReviewRound.objects.create( + article=article1, + round_number=1, + ) assignment = helpers.create_review_assignment( - journal=self.journal_one, - article=article1, - is_complete=False, - review_round=round, - reviewer=self.regular_user, - ) + journal=self.journal_one, + article=article1, + is_complete=False, + review_round=round, + reviewer=self.regular_user, + ) assignment.decision = None assignment.save() self.client.force_login(self.editor) - decline_url = reverse('review_decision', - kwargs={'article_id': article1.pk, - 'decision': 'decline'}) - response = self.client.get(decline_url, - SERVER_NAME=self.journal_one.domain,) + decline_url = reverse( + "review_decision", kwargs={"article_id": article1.pk, "decision": "decline"} + ) + response = self.client.get( + decline_url, + SERVER_NAME=self.journal_one.domain, + ) msg = "The following incomplete reviews will be marked as withdrawn" self.assertContains(response, msg) - data = {'cc': [''], - 'bcc': [''], - 'subject': ['Article Declined'], - 'body': ['Article Declined'], - 'attachments': [''], - 'skip': ['skip']} - response = self.client.post(decline_url, - data, - SERVER_NAME=self.journal_one.domain,) + data = { + "cc": [""], + "bcc": [""], + "subject": ["Article Declined"], + "body": ["Article Declined"], + "attachments": [""], + "skip": ["skip"], + } + response = self.client.post( + decline_url, + data, + SERVER_NAME=self.journal_one.domain, + ) review_set = article1.reviewassignment_set.all() self.assertEqual(review_set.filter(is_complete=True).count(), 1) self.assertEqual(review_set.filter(is_complete=False).count(), 0) @@ -323,9 +330,11 @@ def test_completed_reviews_shared_setting(self): # setup data article_with_completed_reviews = helpers.create_article( self.journal_one, - **{'owner': self.regular_user, - 'title': 'Test Article', - 'stage': submission_models.STAGE_UNDER_REVIEW} + **{ + "owner": self.regular_user, + "title": "Test Article", + "stage": submission_models.STAGE_UNDER_REVIEW, + }, ) round_one, c = review_models.ReviewRound.objects.get_or_create( article=article_with_completed_reviews, @@ -351,37 +360,36 @@ def test_completed_reviews_shared_setting(self): # turn setting on setting_handler.save_setting( - setting_group_name='general', - setting_name='display_completed_reviews_in_additional_rounds', + setting_group_name="general", + setting_name="display_completed_reviews_in_additional_rounds", journal=self.journal_one, - value='On', + value="On", ) clear_cache() # test on self.client.force_login(self.second_user) reversed_url = reverse( - 'do_review', - kwargs={ - 'assignment_id': round_two_active_review.pk, - } - ) - url_with_access_code = f"{reversed_url}?access_code={round_two_active_review.access_code}" + "do_review", + kwargs={ + "assignment_id": round_two_active_review.pk, + }, + ) + url_with_access_code = ( + f"{reversed_url}?access_code={round_two_active_review.access_code}" + ) response = self.client.get( url_with_access_code, SERVER_NAME=self.journal_one.domain, ) - self.assertContains( - response, - f"View Review #{round_one_completed_review.pk}" - ) + self.assertContains(response, f"View Review #{round_one_completed_review.pk}") # turn setting off setting_handler.save_setting( - setting_group_name='general', - setting_name='display_completed_reviews_in_additional_rounds', + setting_group_name="general", + setting_name="display_completed_reviews_in_additional_rounds", journal=self.journal_one, - value='', + value="", ) clear_cache() @@ -392,8 +400,7 @@ def test_completed_reviews_shared_setting(self): SERVER_NAME=self.journal_one.domain, ) self.assertNotContains( - response, - "Please click 'View Review' below to view the peer review report" + response, "Please click 'View Review' below to view the peer review report" ) def test_shared_review_download_view(self): @@ -403,9 +410,11 @@ def test_shared_review_download_view(self): """ article_with_completed_reviews = helpers.create_article( self.journal_one, - **{'owner': self.regular_user, - 'title': 'Test Article', - 'stage': submission_models.STAGE_UNDER_REVIEW} + **{ + "owner": self.regular_user, + "title": "Test Article", + "stage": submission_models.STAGE_UNDER_REVIEW, + }, ) round_one, c = review_models.ReviewRound.objects.get_or_create( article=article_with_completed_reviews, @@ -423,9 +432,9 @@ def test_shared_review_download_view(self): file = files.save_file( request, test_file, - label='Test', + label="Test", public=True, - path_parts=('articles', article_with_completed_reviews.pk), + path_parts=("articles", article_with_completed_reviews.pk), ) round_one_completed_review = helpers.create_review_assignment( journal=self.journal_one, @@ -445,18 +454,18 @@ def test_shared_review_download_view(self): # turn setting on setting_handler.save_setting( - setting_group_name='general', - setting_name='display_completed_reviews_in_additional_rounds', + setting_group_name="general", + setting_name="display_completed_reviews_in_additional_rounds", journal=self.journal_one, - value='On', + value="On", ) clear_cache() download_url = reverse( - 'reviewer_shared_review_download', + "reviewer_shared_review_download", kwargs={ - 'article_id': article_with_completed_reviews.pk, - 'review_id': round_one_completed_review.pk, - } + "article_id": article_with_completed_reviews.pk, + "review_id": round_one_completed_review.pk, + }, ) # in this instance, second_user should have access to download the @@ -518,7 +527,6 @@ def test_shared_review_download_view(self): # finally, delete the file from disk files.delete_file(article_with_completed_reviews, file) - def setup_request_object(self): request = helpers.Request() request.user = self.editor @@ -571,15 +579,27 @@ def setUp(self): :return: None """ self.journal_one, self.journal_two = self.create_journals() - self.create_roles(["editor", "author", "reviewer", "proofreader", - "production", "copyeditor", "typesetter", - "proofing-manager", "section-editor"]) + self.create_roles( + [ + "editor", + "author", + "reviewer", + "proofreader", + "production", + "copyeditor", + "typesetter", + "proofing-manager", + "section-editor", + ] + ) self.regular_user = self.create_user("regularuser@martineve.com") self.regular_user.is_active = True self.regular_user.save() - self.second_user = self.create_user("seconduser@martineve.com", ["reviewer"], journal=self.journal_one) + self.second_user = self.create_user( + "seconduser@martineve.com", ["reviewer"], journal=self.journal_one + ) self.second_user.is_active = True self.second_user.save() @@ -588,48 +608,66 @@ def setUp(self): self.admin_user.is_active = True self.admin_user.save() - self.inactive_user = self.create_user("disableduser@martineve.com", ["editor", "author", "proofreader", - "production"], journal=self.journal_one) + self.inactive_user = self.create_user( + "disableduser@martineve.com", + ["editor", "author", "proofreader", "production"], + journal=self.journal_one, + ) self.inactive_user.is_active = False self.inactive_user.save() - self.editor = self.create_user("editoruser@martineve.com", ["editor"], journal=self.journal_one) + self.editor = self.create_user( + "editoruser@martineve.com", ["editor"], journal=self.journal_one + ) self.editor.is_active = True self.editor.save() - self.author = self.create_user("authoruser@martineve.com", ["author"], journal=self.journal_one) + self.author = self.create_user( + "authoruser@martineve.com", ["author"], journal=self.journal_one + ) self.author.is_active = True self.author.save() - self.proofreader = self.create_user("proofreader@martineve.com", ["proofreader"], journal=self.journal_one) + self.proofreader = self.create_user( + "proofreader@martineve.com", ["proofreader"], journal=self.journal_one + ) self.proofreader.is_active = True self.proofreader.save() - self.proofreader_two = self.create_user("proofreader2@martineve.com", ["proofreader"], journal=self.journal_one) + self.proofreader_two = self.create_user( + "proofreader2@martineve.com", ["proofreader"], journal=self.journal_one + ) self.proofreader_two.is_active = True self.proofreader_two.save() - self.production = self.create_user("production@martineve.com", ["production"], journal=self.journal_one) + self.production = self.create_user( + "production@martineve.com", ["production"], journal=self.journal_one + ) self.production.is_active = True self.production.save() - self.copyeditor = self.create_user("copyeditor@martineve.com", ["copyeditor"], journal=self.journal_one) + self.copyeditor = self.create_user( + "copyeditor@martineve.com", ["copyeditor"], journal=self.journal_one + ) self.copyeditor.is_active = True self.copyeditor.save() - self.typesetter = self.create_user("typesetter@martineve.com", ["typesetter"], journal=self.journal_one) + self.typesetter = self.create_user( + "typesetter@martineve.com", ["typesetter"], journal=self.journal_one + ) self.typesetter.is_active = True self.typesetter.save() - self.other_typesetter = self.create_user("other_typesetter@martineve.com", ["typesetter"], - journal=self.journal_one) + self.other_typesetter = self.create_user( + "other_typesetter@martineve.com", ["typesetter"], journal=self.journal_one + ) self.other_typesetter.is_active = True self.other_typesetter.save() self.proofing_manager = self.create_user( "proofing_manager@martineve.com", ["proofing-manager"], - journal=self.journal_one + journal=self.journal_one, ) self.proofing_manager.is_active = True self.proofing_manager.save() @@ -637,111 +675,141 @@ def setUp(self): self.other_typesetter.is_active = True self.other_typesetter.save() - self.section_editor = self.create_user("section_editor@martineve.com", ['section-editor'], - journal=self.journal_one) + self.section_editor = self.create_user( + "section_editor@martineve.com", ["section-editor"], journal=self.journal_one + ) self.section_editor.is_active = True self.section_editor.save() - self.second_reviewer = self.create_user("second_reviewer@martineve.com", ['reviewer'], - journal=self.journal_one) + self.second_reviewer = self.create_user( + "second_reviewer@martineve.com", ["reviewer"], journal=self.journal_one + ) self.second_reviewer.is_active = True self.second_reviewer.save() - self.public_file = core_models.File(mime_type="A/FILE", - original_filename="blah.txt", - uuid_filename="UUID.txt", - label="A file that is public", - description="Oh yes, it's a file", - owner=self.regular_user, - is_galley=False, - privacy="public") + self.public_file = core_models.File( + mime_type="A/FILE", + original_filename="blah.txt", + uuid_filename="UUID.txt", + label="A file that is public", + description="Oh yes, it's a file", + owner=self.regular_user, + is_galley=False, + privacy="public", + ) self.public_file.save() - self.private_file = core_models.File(mime_type="A/FILE", - original_filename="blah.txt", - uuid_filename="UUID.txt", - label="A file that is private", - description="Oh yes, it's a file", - owner=self.regular_user, - is_galley=False, - privacy="owner") + self.private_file = core_models.File( + mime_type="A/FILE", + original_filename="blah.txt", + uuid_filename="UUID.txt", + label="A file that is private", + description="Oh yes, it's a file", + owner=self.regular_user, + is_galley=False, + privacy="owner", + ) self.private_file.save() - self.article_in_production = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_TYPESETTING, - journal_id=self.journal_one.id) + self.article_in_production = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_TYPESETTING, + journal_id=self.journal_one.id, + ) self.article_in_production.save() self.article_in_production.data_figure_files.add(self.public_file) - self.article_unsubmitted = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNSUBMITTED, - journal_id=self.journal_one.id) + self.article_unsubmitted = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNSUBMITTED, + journal_id=self.journal_one.id, + ) self.article_unsubmitted.save() - self.article_unassigned = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNASSIGNED, - journal_id=self.journal_one.id) + self.article_unassigned = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNASSIGNED, + journal_id=self.journal_one.id, + ) self.article_unassigned.save() - self.article_assigned = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ASSIGNED, - journal_id=self.journal_one.id) + self.article_assigned = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ASSIGNED, + journal_id=self.journal_one.id, + ) self.article_assigned.save() - self.article_under_review = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNDER_REVIEW, - journal_id=self.journal_one.id) + self.article_under_review = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNDER_REVIEW, + journal_id=self.journal_one.id, + ) self.article_under_review.save() - self.article_review_completed = submission_models.Article.objects.create(owner=self.regular_user, - title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id, - date_accepted=timezone.now()) + self.article_review_completed = submission_models.Article.objects.create( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + date_accepted=timezone.now(), + ) - self.article_author_is_owner = submission_models.Article.objects.create(owner=self.author, - title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id, - date_accepted=timezone.now()) + self.article_author_is_owner = submission_models.Article.objects.create( + owner=self.author, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + date_accepted=timezone.now(), + ) self.article_author_is_owner.authors.add(self.editor) self.article_author_is_owner.authors.add(self.author) - self.review_form = review_models.ReviewForm(name="A Form", intro="i", thanks="t", - journal=self.journal_one) + self.review_form = review_models.ReviewForm( + name="A Form", intro="i", thanks="t", journal=self.journal_one + ) self.review_form.save() - self.review_form_element, c = review_models.ReviewFormElement.objects.get_or_create( - name='Review', - kind='text', - order=1, - width='full', - required=True, + self.review_form_element, c = ( + review_models.ReviewFormElement.objects.get_or_create( + name="Review", + kind="text", + order=1, + width="full", + required=True, + ) ) self.review_form.elements.add(self.review_form_element) - self.second_review_form_element, c = review_models.ReviewFormElement.objects.get_or_create( - name='Second Review Form Element', - kind='text', - order=2, - width='full', - required=True, + self.second_review_form_element, c = ( + review_models.ReviewFormElement.objects.get_or_create( + name="Second Review Form Element", + kind="text", + order=2, + width="full", + required=True, + ) ) self.review_form.elements.add(self.second_review_form_element) setting_handler.save_setting( - 'general', - 'enable_save_review_progress', + "general", + "enable_save_review_progress", self.journal_one, - 'On', + "On", ) self.round_one = review_models.ReviewRound.objects.create( @@ -762,157 +830,210 @@ def setUp(self): form=self.review_form, is_complete=True, date_complete=timezone.now(), - decision='accept', + decision="accept", ) self.review_assignment_complete.save() - self.review_assignment_withdrawn = review_models.ReviewAssignment.objects.create( - article=self.article_review_completed, - review_round=self.round_two, - reviewer=self.second_reviewer, - editor=self.editor, - date_due=timezone.now(), - form=self.review_form, - is_complete=True, - decision='withdrawn', + self.review_assignment_withdrawn = ( + review_models.ReviewAssignment.objects.create( + article=self.article_review_completed, + review_round=self.round_two, + reviewer=self.second_reviewer, + editor=self.editor, + date_due=timezone.now(), + form=self.review_form, + is_complete=True, + decision="withdrawn", + ) ) - self.review_assignment_declined, created = review_models.ReviewAssignment.objects.get_or_create( - article=self.article_review_completed, - review_round=self.round_two, - reviewer=self.second_reviewer, + self.review_assignment_declined, created = ( + review_models.ReviewAssignment.objects.get_or_create( + article=self.article_review_completed, + review_round=self.round_two, + reviewer=self.second_reviewer, + editor=self.editor, + date_due=timezone.now(), + date_declined=timezone.now(), + form=self.review_form, + is_complete=False, + ) + ) + + self.review_assignment = review_models.ReviewAssignment( + article=self.article_under_review, + reviewer=self.second_user, editor=self.editor, date_due=timezone.now(), - date_declined=timezone.now(), form=self.review_form, - is_complete=False, ) - self.review_assignment = review_models.ReviewAssignment(article=self.article_under_review, - reviewer=self.second_user, - editor=self.editor, - date_due=timezone.now(), - form=self.review_form) - self.review_assignment.save() - self.review_assignment_not_in_scope = review_models.ReviewAssignment(article=self.article_in_production, - reviewer=self.regular_user, - editor=self.editor, - date_due=timezone.now(), - form=self.review_form) + self.review_assignment_not_in_scope = review_models.ReviewAssignment( + article=self.article_in_production, + reviewer=self.regular_user, + editor=self.editor, + date_due=timezone.now(), + form=self.review_form, + ) self.review_assignment_not_in_scope.save() - self.article_under_revision = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNDER_REVISION, - journal_id=self.journal_one.id) + self.article_under_revision = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNDER_REVISION, + journal_id=self.journal_one.id, + ) self.article_under_revision.save() - self.article_rejected = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_REJECTED, - journal_id=self.journal_one.id) + self.article_rejected = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_REJECTED, + journal_id=self.journal_one.id, + ) self.article_rejected.save() - self.article_accepted = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id) + self.article_accepted = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + ) self.article_accepted.save() - self.section_editor_assignment = review_models.EditorAssignment(article=self.article_assigned, - editor=self.section_editor, - editor_type='section-editor', - notified=True) + self.section_editor_assignment = review_models.EditorAssignment( + article=self.article_assigned, + editor=self.section_editor, + editor_type="section-editor", + notified=True, + ) self.section_editor_assignment.save() - self.article_editor_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_EDITOR_COPYEDITING, - journal_id=self.journal_one.id) + self.article_editor_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_EDITOR_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_editor_copyediting.save() - self.article_author_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_AUTHOR_COPYEDITING, - journal_id=self.journal_one.id) + self.article_author_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_AUTHOR_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_author_copyediting.save() - self.article_final_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_FINAL_COPYEDITING, - journal_id=self.journal_one.id) + self.article_final_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_FINAL_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_final_copyediting.save() - self.article_proofing = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_PROOFING, - journal_id=self.journal_one.id) + self.article_proofing = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_PROOFING, + journal_id=self.journal_one.id, + ) self.article_proofing.save() - assigned = production_models.ProductionAssignment(article=self.article_in_production, - production_manager=self.production) + assigned = production_models.ProductionAssignment( + article=self.article_in_production, production_manager=self.production + ) assigned.save() - self.article_published = submission_models.Article(owner=self.regular_user, title="A Second Test Article", - abstract="An abstract", - stage=submission_models.STAGE_PUBLISHED, - journal_id=self.journal_one.id) + self.article_published = submission_models.Article( + owner=self.regular_user, + title="A Second Test Article", + abstract="An abstract", + stage=submission_models.STAGE_PUBLISHED, + journal_id=self.journal_one.id, + ) self.article_published.save() - assigned = production_models.ProductionAssignment(article=self.article_published, - production_manager=self.production) + assigned = production_models.ProductionAssignment( + article=self.article_published, production_manager=self.production + ) assigned.save() - self.article_in_production_inactive = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_TYPESETTING, - journal_id=self.journal_one.id) + self.article_in_production_inactive = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_TYPESETTING, + journal_id=self.journal_one.id, + ) self.article_in_production_inactive.save() - self.assigned = production_models.ProductionAssignment(article=self.article_in_production_inactive, - production_manager=self.inactive_user) + self.assigned = production_models.ProductionAssignment( + article=self.article_in_production_inactive, + production_manager=self.inactive_user, + ) self.assigned.save() - self.typeset_task = production_models.TypesetTask(assignment=self.assigned, - typesetter=self.typesetter, - notified=True, - accepted=timezone.now()) + self.typeset_task = production_models.TypesetTask( + assignment=self.assigned, + typesetter=self.typesetter, + notified=True, + accepted=timezone.now(), + ) self.typeset_task.save() - self.other_typeset_task = production_models.TypesetTask(assignment=self.assigned, - typesetter=self.other_typesetter, - notified=True, - accepted=timezone.now()) + self.other_typeset_task = production_models.TypesetTask( + assignment=self.assigned, + typesetter=self.other_typesetter, + notified=True, + accepted=timezone.now(), + ) self.other_typeset_task.save() - self.proofing_assignment = proofing_models.ProofingAssignment(article=self.article_proofing, - proofing_manager=self.proofing_manager, - notified=True) + self.proofing_assignment = proofing_models.ProofingAssignment( + article=self.article_proofing, + proofing_manager=self.proofing_manager, + notified=True, + ) self.proofing_assignment.save() self.proofing_assignment.add_new_proofing_round() - self.proofing_task = proofing_models.ProofingTask(round=self.proofing_assignment.current_proofing_round(), - proofreader=self.proofreader, - notified=True, - due=timezone.now(), - accepted=timezone.now(), - task='sdfsdffs') + self.proofing_task = proofing_models.ProofingTask( + round=self.proofing_assignment.current_proofing_round(), + proofreader=self.proofreader, + notified=True, + due=timezone.now(), + accepted=timezone.now(), + task="sdfsdffs", + ) self.proofing_task.save() - self.correction_task = proofing_models.TypesetterProofingTask(proofing_task=self.proofing_task, - typesetter=self.typesetter, - notified=True, - due=timezone.now(), - accepted=timezone.now(), - task='fsddsff') + self.correction_task = proofing_models.TypesetterProofingTask( + proofing_task=self.proofing_task, + typesetter=self.typesetter, + notified=True, + due=timezone.now(), + accepted=timezone.now(), + task="fsddsff", + ) self.correction_task.save() - self.journal_one.name = 'Journal One' - self.journal_two.name = 'Journal Two' - self.press = press_models.Press.objects.create(name='Press', domain='localhost', main_contact='a@b.com') + self.journal_one.name = "Journal One" + self.journal_two.name = "Journal Two" + self.press = press_models.Press.objects.create( + name="Press", domain="localhost", main_contact="a@b.com" + ) self.press.save() update_settings( self.journal_one, @@ -927,22 +1048,22 @@ def test_request_revisions_context(self): self.client.force_login(self.editor) response = self.client.get( reverse( - 'review_request_revisions', - kwargs={'article_id': self.article_review_completed.pk}, + "review_request_revisions", + kwargs={"article_id": self.article_review_completed.pk}, ), SERVER_NAME=self.journal_one.domain, ) - response.context.get('incomplete') + response.context.get("incomplete") self.assertEqual( self.article_review_completed, - response.context.get('article'), + response.context.get("article"), ) # This test does not cover the revision request form self.assertEqual( 0, - response.context.get('pending_approval').count(), + response.context.get("pending_approval").count(), ) self.assertEqual( 0, - response.context.get('incomplete').count(), + response.context.get("incomplete").count(), ) diff --git a/src/review/urls.py b/src/review/urls.py index 692d4569c9..41027b4e06 100755 --- a/src/review/urls.py +++ b/src/review/urls.py @@ -10,157 +10,287 @@ from review.const import EditorialDecisions urlpatterns = [ - re_path(r'^$', views.home, name='review_home'), - re_path(r'^unassigned/$', views.unassigned, name='review_unassigned'), + re_path(r"^$", views.home, name="review_home"), + re_path(r"^unassigned/$", views.unassigned, name="review_unassigned"), re_path( - r'^unassigned/article/(?P\d+)/$', + r"^unassigned/article/(?P\d+)/$", views.unassigned_article, - name='review_unassigned_article', + name="review_unassigned_article", ), re_path( - r'^unassigned/article/(?P\d+)/projected_issue/$', + r"^unassigned/article/(?P\d+)/projected_issue/$", views.add_projected_issue, - name='review_projected_issue', - ), - re_path(r'^unassigned/article/(?P\d+)/assign/(?P\d+)/type/(?P[-\w.]+)/$', - views.assign_editor, name='review_assign_editor'), - re_path(r'^unassigned/article/(?P\d+)/assign/(?P\d+)/type/(?P[-\w.]+)/' - r'move/review/$', - views.assign_editor_move_to_review, name='review_assign_editor_and_move_to_review'), - re_path(r'^unassigned/article/(?P\d+)/unassign/(?P\d+)/$', views.unassign_editor, - name='review_unassign_editor'), - re_path(r'^unassigned/article/(?P\d+)/notify/(?P\d+)/$', views.assignment_notification, - name='review_assignment_notification'), - re_path(r'^unassigned/article/(?P\d+)/move/review/$', views.move_to_review, name='review_move_to_review'), - re_path(r'^article/(?P\d+)/crosscheck/$', views.view_ithenticate_report, name='review_crosscheck'), - re_path(r'^article/(?P\d+)/move/(?Paccept|decline|undecline)/$', views.review_decision, - name='review_decision'), - re_path(r'^article/(?P\d+)/summary/$', views.unassigned_article, name='review_summary'), - re_path(r'^article/(?P\d+)/$', views.in_review, name='review_in_review'), - re_path(r'^article/(?P\d+)/round/(?P\d+)/delete/$', views.delete_review_round, - name='review_delete_review_round'), - re_path(r'^article/(?P\d+)/round/(?P\d+)/files/add/$', views.add_files, name='review_add_files'), - re_path(r'^article/(?P\d+)/round/(?P\d+)/files/(?P\d+)/remove/$', views.remove_file, - name='review_remove_file'), - - re_path(r'^article/(?P\d+)/review/add/$', views.add_review_assignment, name='review_add_review_assignment'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/notify/$', views.notify_reviewer, - name='review_notify_reviewer'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/view/$', views.view_review, - name='review_view_review'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/answer/(?P\d+)/$', views.edit_review_answer, - name='review_edit_review_answer'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/edit/$', views.edit_review, - name='review_edit_review'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/delete/$', views.delete_review, - name='review_delete_review'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/withdraw/$', views.withdraw_review, - name='review_withdraw_review'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/reset/$', views.reset_review, - name='review_reset_review'), - re_path(r'^article/(?P\d+)/review/(?P\d+)/rate/$', views.rate_reviewer, - name='review_rate_reviewer'), - re_path(r'article/(?P\d+)/review/(?P\d+)/reminder/(?Prequest|accepted)/', + name="review_projected_issue", + ), + re_path( + r"^unassigned/article/(?P\d+)/assign/(?P\d+)/type/(?P[-\w.]+)/$", + views.assign_editor, + name="review_assign_editor", + ), + re_path( + r"^unassigned/article/(?P\d+)/assign/(?P\d+)/type/(?P[-\w.]+)/" + r"move/review/$", + views.assign_editor_move_to_review, + name="review_assign_editor_and_move_to_review", + ), + re_path( + r"^unassigned/article/(?P\d+)/unassign/(?P\d+)/$", + views.unassign_editor, + name="review_unassign_editor", + ), + re_path( + r"^unassigned/article/(?P\d+)/notify/(?P\d+)/$", + views.assignment_notification, + name="review_assignment_notification", + ), + re_path( + r"^unassigned/article/(?P\d+)/move/review/$", + views.move_to_review, + name="review_move_to_review", + ), + re_path( + r"^article/(?P\d+)/crosscheck/$", + views.view_ithenticate_report, + name="review_crosscheck", + ), + re_path( + r"^article/(?P\d+)/move/(?Paccept|decline|undecline)/$", + views.review_decision, + name="review_decision", + ), + re_path( + r"^article/(?P\d+)/summary/$", + views.unassigned_article, + name="review_summary", + ), + re_path( + r"^article/(?P\d+)/$", views.in_review, name="review_in_review" + ), + re_path( + r"^article/(?P\d+)/round/(?P\d+)/delete/$", + views.delete_review_round, + name="review_delete_review_round", + ), + re_path( + r"^article/(?P\d+)/round/(?P\d+)/files/add/$", + views.add_files, + name="review_add_files", + ), + re_path( + r"^article/(?P\d+)/round/(?P\d+)/files/(?P\d+)/remove/$", + views.remove_file, + name="review_remove_file", + ), + re_path( + r"^article/(?P\d+)/review/add/$", + views.add_review_assignment, + name="review_add_review_assignment", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/notify/$", + views.notify_reviewer, + name="review_notify_reviewer", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/view/$", + views.view_review, + name="review_view_review", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/answer/(?P\d+)/$", + views.edit_review_answer, + name="review_edit_review_answer", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/edit/$", + views.edit_review, + name="review_edit_review", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/delete/$", + views.delete_review, + name="review_delete_review", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/withdraw/$", + views.withdraw_review, + name="review_withdraw_review", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/reset/$", + views.reset_review, + name="review_reset_review", + ), + re_path( + r"^article/(?P\d+)/review/(?P\d+)/rate/$", + views.rate_reviewer, + name="review_rate_reviewer", + ), + re_path( + r"article/(?P\d+)/review/(?P\d+)/reminder/(?Prequest|accepted)/", views.send_review_reminder, - name='review_send_reminder'), - - re_path(r'^article/(?P\d+)/revisions/request/$', views.request_revisions, - name='review_request_revisions'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/notify/$', views.request_revisions_notification, - name='request_revisions_notification'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/edit/$', views.edit_revision_request, - name='edit_revision_request'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/$', views.do_revisions, - name='do_revisions'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/update/file/(?P\d+)$', - views.replace_file, name='revisions_replace_file'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/upload/file/$', - views.upload_new_file, name='revisions_upload_new_file'), - re_path(r'^article/(?P\d+)/revisions/(?P\d+)/view/$', - views.view_revision, name='view_revision'), - - re_path(r'^article/(?P\d+)/decision/draft/$', views.draft_decision, - name='review_draft_decision'), - re_path(r'^article/(?P\d+)/decision/draft/(?P\d+)/$', views.edit_draft_decision, - name='review_edit_draft_decision'), - re_path(r'^article/(?P\d+)/decision/draft/(?P\d+)/action/$', views.manage_draft, - name='review_manage_draft'), - re_path(r'^article/(?P\d+)/decision/draft/text/$', views.draft_decision_text, - name='review_draft_decision_text'), - - re_path(r'^requests/$', views.review_requests, name='review_requests'), - re_path(r'^requests/(?P\d+)/accept/$', views.accept_review_request, name='accept_review'), - re_path(r'^requests/(?P\d+)/decline/$', views.decline_review_request, name='decline_review'), - re_path(r'^requests/(?P\d+)/decline/suggest/$', views.suggest_reviewers, name='suggest_reviewers'), - re_path(r'^requests/(?P\d+)/thanks/$', views.thanks_review, name='thanks_review'), - re_path(r'^requests/(?P\d+)/annotation/$', views.hypothesis_review, name='hypothesis_review'), - re_path(r'^requests/(?P\d+)/$', - views.do_review, - name='do_review'), - re_path(r'^requests/(?P\d+)/upload_file/$', + name="review_send_reminder", + ), + re_path( + r"^article/(?P\d+)/revisions/request/$", + views.request_revisions, + name="review_request_revisions", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/notify/$", + views.request_revisions_notification, + name="request_revisions_notification", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/edit/$", + views.edit_revision_request, + name="edit_revision_request", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/$", + views.do_revisions, + name="do_revisions", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/update/file/(?P\d+)$", + views.replace_file, + name="revisions_replace_file", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/upload/file/$", + views.upload_new_file, + name="revisions_upload_new_file", + ), + re_path( + r"^article/(?P\d+)/revisions/(?P\d+)/view/$", + views.view_revision, + name="view_revision", + ), + re_path( + r"^article/(?P\d+)/decision/draft/$", + views.draft_decision, + name="review_draft_decision", + ), + re_path( + r"^article/(?P\d+)/decision/draft/(?P\d+)/$", + views.edit_draft_decision, + name="review_edit_draft_decision", + ), + re_path( + r"^article/(?P\d+)/decision/draft/(?P\d+)/action/$", + views.manage_draft, + name="review_manage_draft", + ), + re_path( + r"^article/(?P\d+)/decision/draft/text/$", + views.draft_decision_text, + name="review_draft_decision_text", + ), + re_path(r"^requests/$", views.review_requests, name="review_requests"), + re_path( + r"^requests/(?P\d+)/accept/$", + views.accept_review_request, + name="accept_review", + ), + re_path( + r"^requests/(?P\d+)/decline/$", + views.decline_review_request, + name="decline_review", + ), + re_path( + r"^requests/(?P\d+)/decline/suggest/$", + views.suggest_reviewers, + name="suggest_reviewers", + ), + re_path( + r"^requests/(?P\d+)/thanks/$", + views.thanks_review, + name="thanks_review", + ), + re_path( + r"^requests/(?P\d+)/annotation/$", + views.hypothesis_review, + name="hypothesis_review", + ), + re_path(r"^requests/(?P\d+)/$", views.do_review, name="do_review"), + re_path( + r"^requests/(?P\d+)/upload_file/$", views.upload_review_file, - name='upload_review_file'), - - - re_path(r'^author/(?P\d+)/$', views.author_view_reviews, name='review_author_view'), - - re_path(r'^editor/(?P\d+)/file_download/(?P\d+)/$', views.editor_article_file, - name='editor_file_download'), - - re_path(r'^reviewer/(?P\d+)/file_download/(?P\d+)/$', views.reviewer_article_file, - name='review_file_download'), - re_path(r'^reviewer/(?P\d+)/file_download/all/$', views.review_download_all_files, - name='review_download_all_files'), - + name="upload_review_file", + ), + re_path( + r"^author/(?P\d+)/$", + views.author_view_reviews, + name="review_author_view", + ), + re_path( + r"^editor/(?P\d+)/file_download/(?P\d+)/$", + views.editor_article_file, + name="editor_file_download", + ), + re_path( + r"^reviewer/(?P\d+)/file_download/(?P\d+)/$", + views.reviewer_article_file, + name="review_file_download", + ), + re_path( + r"^reviewer/(?P\d+)/file_download/all/$", + views.review_download_all_files, + name="review_download_all_files", + ), re_path( - r'^article/(?P\d+)/decision/(?P{decision_options})/access_denied/$'.format( - decision_options="|".join( - decision.value for decision in EditorialDecisions) + r"^article/(?P\d+)/decision/(?P{decision_options})/access_denied/$".format( + decision_options="|".join(decision.value for decision in EditorialDecisions) ), views.review_warning, - name='review_warning'), - + name="review_warning", + ), # Review forms - re_path(r'^manager/forms/$', - views.review_forms, - name='review_review_forms'), - re_path(r'^manager/form/(?P\d+)/$', + re_path(r"^manager/forms/$", views.review_forms, name="review_review_forms"), + re_path( + r"^manager/form/(?P\d+)/$", views.edit_review_form, - name='edit_review_form'), - re_path(r'^manager/form/(?P\d+)/preview/$', + name="edit_review_form", + ), + re_path( + r"^manager/form/(?P\d+)/preview/$", views.preview_form, - name='preview_form'), - re_path(r'^manager/form/(?P\d+)/order_elements/$', + name="preview_form", + ), + re_path( + r"^manager/form/(?P\d+)/order_elements/$", views.order_review_elements, - name='order_review_elements'), - re_path(r'^manager/form/(?P\d+)/element/(?P\d+)/$', + name="order_review_elements", + ), + re_path( + r"^manager/form/(?P\d+)/element/(?P\d+)/$", views.edit_review_form, - name='edit_review_form_element'), - - re_path(r'^article/(?P\d+)/decision_helper/$', + name="edit_review_form_element", + ), + re_path( + r"^article/(?P\d+)/decision_helper/$", views.decision_helper, - name='decision_helper', + name="decision_helper", ), re_path( - r'^article/(?P\d+)/csv-import/$', + r"^article/(?P\d+)/csv-import/$", views.upload_reviewers_from_csv, - name='upload_reviewers_from_csv', + name="upload_reviewers_from_csv", ), - # Review Sharing re_path( - r'^article/(?P\d+)/share/$', + r"^article/(?P\d+)/share/$", views.editor_share_reviews, - name='editor_share_reviews', + name="editor_share_reviews", ), re_path( - r'^article/(?P\d+)/share/reviewer/$', + r"^article/(?P\d+)/share/reviewer/$", views.reviewer_share_reviews, - name='reviewer_share_reviews', + name="reviewer_share_reviews", ), re_path( - r'^article/(?P\d+)/share/download/(?P\d+)/$', + r"^article/(?P\d+)/share/download/(?P\d+)/$", views.reviewer_shared_review_download, - name='reviewer_shared_review_download', + name="reviewer_shared_review_download", ), ] diff --git a/src/review/views.py b/src/review/views.py index aa13351be9..2400ce6dde 100755 --- a/src/review/views.py +++ b/src/review/views.py @@ -23,22 +23,28 @@ from django.utils.translation import gettext_lazy as _ from core import ( - files, - forms as core_forms, - logic as core_logic, - models as core_models, + files, + forms as core_forms, + logic as core_logic, + models as core_models, ) from events import logic as event_logic from review import models, logic, forms, hypothesis from review.const import EditorialDecisions as ED from security.decorators import ( - editor_user_required, reviewer_user_required, + editor_user_required, + reviewer_user_required, reviewer_user_for_assignment_required, - file_user_required, article_decision_not_made, article_author_required, - editor_is_not_author, senior_editor_user_required, - section_editor_draft_decisions, article_stage_review_required, - any_editor_user_required, setting_is_enabled, - user_has_completed_review_for_article + file_user_required, + article_decision_not_made, + article_author_required, + editor_is_not_author, + senior_editor_user_required, + section_editor_draft_decisions, + article_stage_review_required, + any_editor_user_required, + setting_is_enabled, + user_has_completed_review_for_article, ) from submission import models as submission_models, forms as submission_forms from utils import models as util_models, ithenticate, shared, setting_handler @@ -55,24 +61,24 @@ def home(request): :return: HttpResponse """ articles = submission_models.Article.objects.filter( - Q(stage=submission_models.STAGE_ASSIGNED) | - Q(stage=submission_models.STAGE_UNDER_REVIEW) | - Q(stage=submission_models.STAGE_UNDER_REVISION) | - Q(stage=submission_models.STAGE_ACCEPTED), - journal=request.journal + Q(stage=submission_models.STAGE_ASSIGNED) + | Q(stage=submission_models.STAGE_UNDER_REVIEW) + | Q(stage=submission_models.STAGE_UNDER_REVISION) + | Q(stage=submission_models.STAGE_ACCEPTED), + journal=request.journal, ) - filter = request.GET.get('filter', None) - if filter == 'me': + filter = request.GET.get("filter", None) + if filter == "me": articles = core_logic.filter_articles_to_editor_assigned(request, articles) if not request.user.is_editor(request) and request.user.is_section_editor(request): articles = core_logic.filter_articles_to_editor_assigned(request, articles) - template = 'review/home.html' + template = "review/home.html" context = { - 'articles': articles, - 'filter': filter, + "articles": articles, + "filter": filter, } return render(request, template, context) @@ -85,15 +91,16 @@ def unassigned(request): :param request: HttpRequest object :return: HttpResponse """ - articles = submission_models.Article.objects.filter(stage=submission_models.STAGE_UNASSIGNED, - journal=request.journal) + articles = submission_models.Article.objects.filter( + stage=submission_models.STAGE_UNASSIGNED, journal=request.journal + ) if not request.user.is_editor(request) and request.user.is_section_editor(request): articles = core_logic.filter_articles_to_editor_assigned(request, articles) - template = 'review/unassigned.html' + template = "review/unassigned.html" context = { - 'articles': articles, + "articles": articles, } return render(request, template, context) @@ -116,8 +123,8 @@ def unassigned_article(request, article_id): if article.ithenticate_id and not article.ithenticate_score: ithenticate.fetch_percentage(request.journal, [article]) - if 'crosscheck' in request.POST: - file_id = request.POST.get('crosscheck') + if "crosscheck" in request.POST: + file_id = request.POST.get("crosscheck") file = get_object_or_404(core_models.File, pk=file_id) try: id = ithenticate.send_to_ithenticate(article, file) @@ -127,32 +134,32 @@ def unassigned_article(request, article_id): messages.add_message( request, messages.ERROR, - 'Error returned by iThenticate. ' - 'Check login details and API status.', + "Error returned by iThenticate. " "Check login details and API status.", ) return redirect( reverse( - 'review_unassigned_article', - kwargs={'article_id': article.pk}, + "review_unassigned_article", + kwargs={"article_id": article.pk}, ) ) - current_editors = [assignment.editor.pk for assignment in - models.EditorAssignment.objects.filter(article=article)] + current_editors = [ + assignment.editor.pk + for assignment in models.EditorAssignment.objects.filter(article=article) + ] editors = core_models.AccountRole.objects.filter( - role__slug='editor', - journal=request.journal).exclude(user__id__in=current_editors) + role__slug="editor", journal=request.journal + ).exclude(user__id__in=current_editors) section_editors = core_models.AccountRole.objects.filter( - role__slug='section-editor', - journal=request.journal + role__slug="section-editor", journal=request.journal ).exclude(user__id__in=current_editors) - template = 'review/unassigned_article.html' + template = "review/unassigned_article.html" context = { - 'article': article, - 'editors': editors, - 'section_editors': section_editors, + "article": article, + "editors": editors, + "section_editors": section_editors, } return render(request, template, context) @@ -182,25 +189,25 @@ def add_projected_issue(request, article_id): messages.add_message( request, messages.SUCCESS, - 'Projected Issue set.', + "Projected Issue set.", ) - if request.GET.get('return'): + if request.GET.get("return"): return redirect( - request.GET.get('return'), + request.GET.get("return"), ) else: return redirect( reverse( - 'review_projected_issue', - kwargs={'article_id': article.pk}, + "review_projected_issue", + kwargs={"article_id": article.pk}, ) ) - template = 'review/projected_issue.html' + template = "review/projected_issue.html" context = { - 'article': article, - 'form': form, + "article": article, + "form": form, } return render(request, template, context) @@ -221,9 +228,9 @@ def view_ithenticate_report(request, article_id): if ithenticate_url: return redirect(ithenticate_url) - template = 'review/ithenticate_failure.html' + template = "review/ithenticate_failure.html" context = { - 'article': article, + "article": article, } return render(request, template, context) @@ -232,12 +239,16 @@ def view_ithenticate_report(request, article_id): @senior_editor_user_required def assign_editor_move_to_review(request, article_id, editor_id, assignment_type): """Allows an editor to assign another editor to an article and moves to review.""" - assign_editor(request, article_id, editor_id, assignment_type, should_redirect=False) + assign_editor( + request, article_id, editor_id, assignment_type, should_redirect=False + ) return move_to_review(request, article_id) @senior_editor_user_required -def assign_editor(request, article_id, editor_id, assignment_type, should_redirect=True): +def assign_editor( + request, article_id, editor_id, assignment_type, should_redirect=True +): """ Allows a Senior Editor to assign another editor to an article. :param request: HttpRequest object @@ -255,20 +266,37 @@ def assign_editor(request, article_id, editor_id, assignment_type, should_redire editor = get_object_or_404(core_models.Account, pk=editor_id) if not editor.has_an_editor_role(request): - messages.add_message(request, messages.WARNING, 'User is not an Editor or Section Editor') - return redirect(reverse('review_unassigned_article', kwargs={'article_id': article.pk})) + messages.add_message( + request, messages.WARNING, "User is not an Editor or Section Editor" + ) + return redirect( + reverse("review_unassigned_article", kwargs={"article_id": article.pk}) + ) _, created = logic.assign_editor(article, editor, assignment_type, request) - messages.add_message(request, messages.SUCCESS, '{0} added as an Editor'.format(editor.full_name())) + messages.add_message( + request, messages.SUCCESS, "{0} added as an Editor".format(editor.full_name()) + ) if created and should_redirect: - return redirect('{0}?return={1}'.format( - reverse('review_assignment_notification', kwargs={'article_id': article_id, 'editor_id': editor.pk}), - request.GET.get('return'))) + return redirect( + "{0}?return={1}".format( + reverse( + "review_assignment_notification", + kwargs={"article_id": article_id, "editor_id": editor.pk}, + ), + request.GET.get("return"), + ) + ) elif not created: - messages.add_message(request, messages.WARNING, - '{0} is already an Editor on this article.'.format(editor.full_name())) + messages.add_message( + request, + messages.WARNING, + "{0} is already an Editor on this article.".format(editor.full_name()), + ) if should_redirect: - return redirect(reverse('review_unassigned_article', kwargs={'article_id': article_id})) + return redirect( + reverse("review_unassigned_article", kwargs={"article_id": article_id}) + ) @senior_editor_user_required @@ -286,50 +314,53 @@ def unassign_editor(request, article_id, editor_id): skip = request.POST.get("skip") email_context = logic.get_unassignment_context(request, assignment) form = core_forms.SettingEmailForm( - setting_name="unassign_editor", - email_context=email_context, - request=request, + setting_name="unassign_editor", + email_context=email_context, + request=request, ) if request.method == "POST": form = core_forms.SettingEmailForm( - request.POST, request.FILES, - setting_name="unassign_editor", - email_context=email_context, - request=request, + request.POST, + request.FILES, + setting_name="unassign_editor", + email_context=email_context, + request=request, ) if form.is_valid() or skip: kwargs = { - 'email_data': form.as_dataclass(), - 'assignment': assignment, - 'request': request, - 'skip': skip, + "email_data": form.as_dataclass(), + "assignment": assignment, + "request": request, + "skip": skip, } event_logic.Events.raise_event( - event_logic.Events.ON_ARTICLE_UNASSIGNED, **kwargs) + event_logic.Events.ON_ARTICLE_UNASSIGNED, **kwargs + ) assignment.delete() util_models.LogEntry.add_entry( - types='EditorialAction', - description='Editor {0} unassigned from article {1}' - ''.format(editor.full_name(), article.id), - level='Info', + types="EditorialAction", + description="Editor {0} unassigned from article {1}" "".format( + editor.full_name(), article.id + ), + level="Info", request=request, target=article, ) - return redirect(reverse( - 'review_unassigned_article', kwargs={'article_id': article_id} - )) + return redirect( + reverse("review_unassigned_article", kwargs={"article_id": article_id}) + ) - template = 'review/unassign_editor.html' + template = "review/unassign_editor.html" context = { - 'article': article, - 'assignment': assignment, - 'form': form, + "article": article, + "assignment": assignment, + "form": form, } return render(request, template, context) @@ -350,44 +381,52 @@ def assignment_notification(request, article_id, editor_id): journal=request.journal, ) editor = get_object_or_404(core_models.Account, pk=editor_id) - assignment = get_object_or_404(models.EditorAssignment, article=article, editor=editor, notified=False) + assignment = get_object_or_404( + models.EditorAssignment, article=article, editor=editor, notified=False + ) email_context = logic.get_assignment_context(request, article, editor, assignment) form = core_forms.SettingEmailForm( - setting_name="editor_assignment", - email_context=email_context, - request=request, + setting_name="editor_assignment", + email_context=email_context, + request=request, ) if request.POST: form = core_forms.SettingEmailForm( - request.POST, request.FILES, + request.POST, + request.FILES, setting_name="editor_assignment", email_context=email_context, request=request, ) if form.is_valid(): kwargs = { - 'editor_assignment': assignment, - 'request': request, - 'skip': request.POST.get("skip"), - 'email_data': form.as_dataclass(), + "editor_assignment": assignment, + "request": request, + "skip": request.POST.get("skip"), + "email_data": form.as_dataclass(), } event_logic.Events.raise_event( - event_logic.Events.ON_EDITOR_MANUALLY_ASSIGNED, **kwargs) + event_logic.Events.ON_EDITOR_MANUALLY_ASSIGNED, **kwargs + ) - if request.GET.get('return', None): - return redirect(request.GET.get('return')) + if request.GET.get("return", None): + return redirect(request.GET.get("return")) else: - return redirect(reverse('review_unassigned_article', kwargs={'article_id': article_id})) + return redirect( + reverse( + "review_unassigned_article", kwargs={"article_id": article_id} + ) + ) - template = 'review/assignment_notification.html' + template = "review/assignment_notification.html" context = { - 'article': article_id, - 'editor': editor, - 'assignment': assignment, - 'form': form, + "article": article_id, + "editor": editor, + "assignment": assignment, + "form": form, } return render(request, template, context) @@ -405,19 +444,31 @@ def move_to_review(request, article_id, should_redirect=True): if article.editorassignment_set.all().count() > 0: article.stage = submission_models.STAGE_ASSIGNED article.save() - review_round, created = models.ReviewRound.objects.get_or_create(article=article, round_number=1) + review_round, created = models.ReviewRound.objects.get_or_create( + article=article, round_number=1 + ) if not created: - messages.add_message(request, messages.WARNING, 'A default review round already exists for this article.') + messages.add_message( + request, + messages.WARNING, + "A default review round already exists for this article.", + ) else: - messages.add_message(request, messages.INFO, 'You must assign an editor before moving into reivew.') + messages.add_message( + request, + messages.INFO, + "You must assign an editor before moving into reivew.", + ) if should_redirect: - if request.GET.get('return', None): - return redirect(request.GET.get('return')) + if request.GET.get("return", None): + return redirect(request.GET.get("return")) else: - return redirect("{0}?modal_id={1}".format(reverse('kanban_home'), article_id)) + return redirect( + "{0}?modal_id={1}".format(reverse("kanban_home"), article_id) + ) @editor_is_not_author @@ -439,54 +490,64 @@ def in_review(request, article_id): if not review_rounds: models.ReviewRound.objects.create(article=article, round_number=1) - return redirect(reverse('review_in_review', kwargs={'article_id': article.id})) + return redirect(reverse("review_in_review", kwargs={"article_id": article.id})) if request.POST: - - if 'move_to_review' in request.POST and article.stage == submission_models.STAGE_UNASSIGNED: + if ( + "move_to_review" in request.POST + and article.stage == submission_models.STAGE_UNASSIGNED + ): article.stage = submission_models.STAGE_UNDER_REVIEW article.save() - if 'table_format_reviews' in request.POST: - request.session['table_format_reviews'] = True + if "table_format_reviews" in request.POST: + request.session["table_format_reviews"] = True request.session.modified = True - if 'expanded_format_reviews' in request.POST: - request.session.pop('table_format_reviews') + if "expanded_format_reviews" in request.POST: + request.session.pop("table_format_reviews") request.session.modified = True - if 'new_review_round' in request.POST: - + if "new_review_round" in request.POST: # Complete all existing review assignments. - for assignment in article.current_review_round_object().reviewassignment_set.all(): + for ( + assignment + ) in article.current_review_round_object().reviewassignment_set.all(): if not assignment.date_complete: assignment.date_complete = timezone.now() - assignment.decision = 'withdrawn' + assignment.decision = "withdrawn" assignment.is_complete = True assignment.save() - messages.add_message(request, messages.INFO, 'Assignment {0} closed.'.format(assignment.id)) + messages.add_message( + request, + messages.INFO, + "Assignment {0} closed.".format(assignment.id), + ) - kwargs = {'review_assignment': assignment, - 'request': request} - event_logic.Events.raise_event(event_logic.Events.ON_REVIEW_CLOSED, - task_object=assignment.article, - **kwargs) + kwargs = {"review_assignment": assignment, "request": request} + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEW_CLOSED, + task_object=assignment.article, + **kwargs, + ) # Add a new review round. new_round_number = article.current_review_round() + 1 - models.ReviewRound.objects.create(article=article, round_number=new_round_number) + models.ReviewRound.objects.create( + article=article, round_number=new_round_number + ) article.stage = submission_models.STAGE_UNDER_REVIEW article.save() - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) - template = 'review/in_review.html' + template = "review/in_review.html" context = { - 'article': article, - 'review_rounds': review_rounds, - 'revisions_requests': revisions_requests, - 'review_stages': submission_models.REVIEW_STAGES, + "article": article, + "review_rounds": review_rounds, + "revisions_requests": revisions_requests, + "review_stages": submission_models.REVIEW_STAGES, } return render(request, template, context) @@ -518,62 +579,35 @@ def send_review_reminder(request, article_id, review_id, reminder_type): # If this review has not been accepted, you cannot send an accepted # reminder, add a message and redirect. - if not review_assignment.date_accepted and reminder_type == 'accepted': + if not review_assignment.date_accepted and reminder_type == "accepted": messages.add_message( request, messages.INFO, - 'You cannot send this reminder type. Review not accepted.' - ) - return redirect( - reverse( - 'review_in_review', - kwargs={'article_id': article.pk} - ) + "You cannot send this reminder type. Review not accepted.", ) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) email_content = logic.get_reminder_content( - reminder_type, - article, - review_assignment, - request + reminder_type, article, review_assignment, request ) - form_initials = { - 'body': email_content, - 'subject': 'Review Request Reminder' - } - form = forms.ReviewReminderForm( - initial=form_initials - ) + form_initials = {"body": email_content, "subject": "Review Request Reminder"} + form = forms.ReviewReminderForm(initial=form_initials) if request.POST: - form = forms.ReviewReminderForm( - request.POST - ) + form = forms.ReviewReminderForm(request.POST) if form.is_valid(): - logic.send_review_reminder( - request, - form, - review_assignment, - reminder_type - ) - messages.add_message( - request, - messages.SUCCESS, - 'Email sent' - ) + logic.send_review_reminder(request, form, review_assignment, reminder_type) + messages.add_message(request, messages.SUCCESS, "Email sent") return redirect( - reverse( - 'review_in_review', - kwargs={'article_id': article.pk} - ) + reverse("review_in_review", kwargs={"article_id": article.pk}) ) - template = 'review/send_review_reminder.html' + template = "review/send_review_reminder.html" context = { - 'article': article, - 'assignment': review_assignment, - 'form': form, + "article": article, + "assignment": review_assignment, + "form": form, } return render(request, template, context) @@ -597,24 +631,30 @@ def delete_review_round(request, article_id, round_id): review_round = get_object_or_404(models.ReviewRound, pk=round_id) if request.POST: - if 'delete' in request.POST: + if "delete" in request.POST: review_round.delete() if article.is_under_revision(): article.stage = submission_models.STAGE_UNDER_REVISION article.save() - messages.add_message(request, messages.INFO, 'Round {0} deleted.'.format(review_round.round_number)) - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + messages.add_message( + request, + messages.INFO, + "Round {0} deleted.".format(review_round.round_number), + ) + return redirect( + reverse("review_in_review", kwargs={"article_id": article_id}) + ) elif not review_round.round_number == article.current_review_round(): - messages.add_message(request, messages.INFO, 'Cannot delete a closed round.') - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + messages.add_message(request, messages.INFO, "Cannot delete a closed round.") + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) - template = 'review/delete_review_round.html' + template = "review/delete_review_round.html" context = { - 'article': article, - 'round': review_round, + "article": article, + "round": review_round, } return render(request, template, context) @@ -632,45 +672,56 @@ def add_files(request, article_id, round_id): :return: HttpResponse or HttpRedirect """ article = get_object_or_404( - submission_models.Article.objects.prefetch_related('manuscript_files'), + submission_models.Article.objects.prefetch_related("manuscript_files"), pk=article_id, journal=request.journal, ) review_round = get_object_or_404( - models.ReviewRound.objects.prefetch_related('review_files'), + models.ReviewRound.objects.prefetch_related("review_files"), pk=round_id, ) if request.POST: - - if 'upload' in request.POST: - review_files = request.FILES.getlist('review_file') + if "upload" in request.POST: + review_files = request.FILES.getlist("review_file") if review_files: for review_file in review_files: - new_file_obj = files.save_file_to_article(review_file, article, request.user, 'Review File') + new_file_obj = files.save_file_to_article( + review_file, article, request.user, "Review File" + ) article.manuscript_files.add(new_file_obj) - messages.add_message(request, messages.SUCCESS, 'File uploaded') + messages.add_message(request, messages.SUCCESS, "File uploaded") else: - messages.add_message(request, messages.WARNING, 'No file uploaded.') + messages.add_message(request, messages.WARNING, "No file uploaded.") - return redirect(reverse('review_add_files', kwargs={'article_id': article.pk, 'round_id': review_round.pk})) + return redirect( + reverse( + "review_add_files", + kwargs={"article_id": article.pk, "round_id": review_round.pk}, + ) + ) - for file in request.POST.getlist('file'): + for file in request.POST.getlist("file"): file = core_models.File.objects.get(id=file) review_round.review_files.add(file) - messages.add_message(request, messages.INFO, 'File {0} added.'.format(file.label)) + messages.add_message( + request, messages.INFO, "File {0} added.".format(file.label) + ) - if not request.POST.getlist('file'): - messages.add_message(request, messages.WARNING, - 'Please select at least one file, or press the Cancel button.') + if not request.POST.getlist("file"): + messages.add_message( + request, + messages.WARNING, + "Please select at least one file, or press the Cancel button.", + ) - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) - template = 'review/add_files.html' + template = "review/add_files.html" context = { - 'article': article, - 'round': review_round, + "article": article, + "round": review_round, } return render(request, template, context) @@ -691,11 +742,16 @@ def remove_file(request, article_id, round_id, file_id): if review_round.round_number == article.current_review_round(): review_round.review_files.remove(file) - messages.add_message(request, messages.INFO, 'File {0} removed.'.format(file.label)) + messages.add_message( + request, messages.INFO, "File {0} removed.".format(file.label) + ) else: - messages.add_message(request, messages.INFO, - 'Cannot remove a file from a closed review round.'.format(file.label)) - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + messages.add_message( + request, + messages.INFO, + "Cannot remove a file from a closed review round.".format(file.label), + ) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) @reviewer_user_for_assignment_required @@ -711,29 +767,35 @@ def accept_review_request(request, assignment_id): # update the ReviewAssignment object if access_code: - assignment = models.ReviewAssignment.objects.get(Q(pk=assignment_id) & - Q(is_complete=False) & - Q(access_code=access_code) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(date_accepted__isnull=True)) + assignment = models.ReviewAssignment.objects.get( + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(access_code=access_code) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(date_accepted__isnull=True) + ) else: - assignment = models.ReviewAssignment.objects.get(Q(pk=assignment_id) & - Q(is_complete=False) & - Q(reviewer=request.user) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(date_accepted__isnull=True)) + assignment = models.ReviewAssignment.objects.get( + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(reviewer=request.user) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(date_accepted__isnull=True) + ) assignment.date_accepted = timezone.now() assignment.save() - kwargs = {'review_assignment': assignment, - 'request': request, - 'accepted': True} - event_logic.Events.raise_event(event_logic.Events.ON_REVIEWER_ACCEPTED, - task_object=assignment.article, - **kwargs) + kwargs = {"review_assignment": assignment, "request": request, "accepted": True} + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEWER_ACCEPTED, + task_object=assignment.article, + **kwargs, + ) - return redirect(logic.generate_access_code_url('do_review', assignment, access_code)) + return redirect( + logic.generate_access_code_url("do_review", assignment, access_code) + ) @reviewer_user_for_assignment_required @@ -748,17 +810,17 @@ def decline_review_request(request, assignment_id): if access_code: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(access_code=access_code) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(access_code=access_code) ) else: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(reviewer=request.user) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(reviewer=request.user) ) assignment.date_declined = timezone.now() @@ -766,18 +828,18 @@ def decline_review_request(request, assignment_id): assignment.is_complete = True assignment.save() - template = 'review/review_decline.html' + template = "review/review_decline.html" context = { - 'assigned_articles_for_user_review': assignment, - 'access_code': access_code if access_code else '' + "assigned_articles_for_user_review": assignment, + "access_code": access_code if access_code else "", } - kwargs = {'review_assignment': assignment, - 'request': request, - 'accepted': False} - event_logic.Events.raise_event(event_logic.Events.ON_REVIEWER_DECLINED, - task_object=assignment.article, - **kwargs) + kwargs = {"review_assignment": assignment, "request": request, "accepted": False} + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEWER_DECLINED, + task_object=assignment.article, + **kwargs, + ) return render(request, template, context) @@ -795,20 +857,20 @@ def suggest_reviewers(request, assignment_id): if access_code: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=True) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(access_code=access_code) + Q(pk=assignment_id) + & Q(is_complete=True) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(access_code=access_code) ) else: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=True) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(reviewer=request.user) + Q(pk=assignment_id) + & Q(is_complete=True) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(reviewer=request.user) ) except models.ReviewAssignment.DoesNotExist: - raise PermissionError('Suggested reviewers already supplied.') + raise PermissionError("Suggested reviewers already supplied.") form = forms.SuggestReviewers(instance=assignment) if request.POST: @@ -817,13 +879,17 @@ def suggest_reviewers(request, assignment_id): if form.is_valid(): form.save() - messages.add_message(request, messages.INFO, 'Thanks for suggesting reviewers for this article.') - return redirect(reverse('website_index')) + messages.add_message( + request, + messages.INFO, + "Thanks for suggesting reviewers for this article.", + ) + return redirect(reverse("website_index")) - template = 'review/suggest_reviewers.html' + template = "review/suggest_reviewers.html" context = { - 'assignment': assignment, - 'form': form, + "assignment": assignment, + "form": form, } return render(request, template, context) @@ -837,32 +903,30 @@ def review_requests(request): :return: a context for a Django template """ new_requests = models.ReviewAssignment.objects.filter( - Q(is_complete=False) & - Q(reviewer=request.user) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(date_accepted__isnull=True), - article__journal=request.journal - ).select_related('article') + Q(is_complete=False) + & Q(reviewer=request.user) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(date_accepted__isnull=True), + article__journal=request.journal, + ).select_related("article") active_requests = models.ReviewAssignment.objects.filter( - Q(is_complete=False) & - Q(reviewer=request.user) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW), + Q(is_complete=False) + & Q(reviewer=request.user) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW), Q(date_accepted__isnull=False), - article__journal=request.journal - ).select_related('article') + article__journal=request.journal, + ).select_related("article") completed_requests = models.ReviewAssignment.objects.filter( - Q(is_complete=True) & - Q(reviewer=request.user), - article__journal=request.journal - ).select_related('article') + Q(is_complete=True) & Q(reviewer=request.user), article__journal=request.journal + ).select_related("article") - template = 'review/review_requests.html' + template = "review/review_requests.html" context = { - 'new_requests': new_requests, - 'active_requests': active_requests, - 'completed_requests': completed_requests, + "new_requests": new_requests, + "active_requests": active_requests, + "completed_requests": completed_requests, } return render(request, template, context) @@ -880,30 +944,32 @@ def do_review(request, assignment_id): if access_code: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage__in=submission_models.REVIEW_ACCESSIBLE_STAGES) & - Q(access_code=access_code) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage__in=submission_models.REVIEW_ACCESSIBLE_STAGES) + & Q(access_code=access_code) ) else: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage__in=submission_models.REVIEW_ACCESSIBLE_STAGES) & - Q(reviewer=request.user) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage__in=submission_models.REVIEW_ACCESSIBLE_STAGES) + & Q(reviewer=request.user) ) allow_save_review = setting_handler.get_setting( - 'general', 'enable_save_review_progress', request.journal, + "general", + "enable_save_review_progress", + request.journal, ).processed_value open_review_initial = setting_handler.get_setting( - 'general', - 'open_review_default_opt_in', + "general", + "open_review_default_opt_in", request.journal, ).processed_value recommendation_disabled = setting_handler.get_setting( - 'general', - 'disable_reviewer_recommendation', + "general", + "disable_reviewer_recommendation", request.journal, ).processed_value @@ -922,26 +988,25 @@ def do_review(request, assignment_id): recommendation_disabled=recommendation_disabled, ) - if 'review_file' in request.GET: + if "review_file" in request.GET: return logic.serve_review_file(assignment) if request.POST: if request.FILES: - assignment = upload_review_file( - request, assignment_id=assignment_id) - if 'decline' in request.POST: + assignment = upload_review_file(request, assignment_id=assignment_id) + if "decline" in request.POST: return redirect( logic.generate_access_code_url( - 'decline_review', + "decline_review", assignment, access_code, ) ) - if 'accept' in request.POST: + if "accept" in request.POST: return redirect( logic.generate_access_code_url( - 'accept_review', + "accept_review", assignment, access_code, ) @@ -971,15 +1036,15 @@ def do_review(request, assignment_id): if form.is_valid() and decision_form.is_valid(): decision_form.save() assignment.save_review_form(form, assignment) - if 'save_progress' in request.POST: + if "save_progress" in request.POST: messages.add_message( request, messages.SUCCESS, - 'Progress saved', + "Progress saved", ) return redirect( logic.generate_access_code_url( - 'do_review', + "do_review", assignment, access_code, ) @@ -992,17 +1057,16 @@ def do_review(request, assignment_id): assignment.save() - kwargs = {'review_assignment': assignment, - 'request': request} + kwargs = {"review_assignment": assignment, "request": request} event_logic.Events.raise_event( event_logic.Events.ON_REVIEW_COMPLETE, task_object=assignment.article, - **kwargs + **kwargs, ) return redirect( logic.generate_access_code_url( - 'thanks_review', + "thanks_review", assignment, access_code, ) @@ -1011,17 +1075,17 @@ def do_review(request, assignment_id): messages.add_message( request, messages.ERROR, - 'Found errors on the form. Please, resolve them and try again', + "Found errors on the form. Please, resolve them and try again", ) - template = 'review/review_form.html' + template = "review/review_form.html" context = { - 'assignment': assignment, - 'form': form, - 'decision_form': decision_form, - 'review_round': review_round, - 'access_code': access_code, - 'allow_save_review': allow_save_review, + "assignment": assignment, + "form": form, + "decision_form": decision_form, + "review_round": review_round, + "access_code": access_code, + "allow_save_review": allow_save_review, } return render(request, template, context) @@ -1047,8 +1111,8 @@ def upload_review_file(request, assignment_id): & Q(reviewer=request.user) ) - if 'review_file' in request.FILES: - uploaded_file = request.FILES.get('review_file', None) + if "review_file" in request.FILES: + uploaded_file = request.FILES.get("review_file", None) old_file = assignment.review_file @@ -1064,7 +1128,7 @@ def upload_review_file(request, assignment_id): messages.add_message( request, messages.SUCCESS, - 'File uploaded successfully.', + "File uploaded successfully.", ) if old_file: @@ -1075,7 +1139,7 @@ def upload_review_file(request, assignment_id): messages.add_message( request, messages.ERROR, - 'Please select a file to upload.', + "Please select a file to upload.", ) return assignment @@ -1093,23 +1157,23 @@ def thanks_review(request, assignment_id): if access_code: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=True) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(access_code=access_code) + Q(pk=assignment_id) + & Q(is_complete=True) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(access_code=access_code) ) else: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=True) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(reviewer=request.user) + Q(pk=assignment_id) + & Q(is_complete=True) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(reviewer=request.user) ) - template = 'review/thanks.html' + template = "review/thanks.html" context = { - 'assignment': assignment, - 'access_code': access_code, + "assignment": assignment, + "access_code": access_code, } return render(request, template, context) @@ -1147,28 +1211,33 @@ def add_review_assignment(request, article_id): messages.add_message( request, messages.WARNING, - 'You should select files for review before adding reviewers.', + "You should select files for review before adding reviewers.", ) - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) if request.POST: - - if 'assign' in request.POST: + if "assign" in request.POST: # first check whether the user exists new_reviewer_form = core_forms.QuickUserForm(request.POST) try: - user = core_models.Account.objects.get(email=new_reviewer_form.data['email']) - user.add_account_role('reviewer', request.journal) + user = core_models.Account.objects.get( + email=new_reviewer_form.data["email"] + ) + user.add_account_role("reviewer", request.journal) except core_models.Account.DoesNotExist: user = None if user: return redirect( reverse( - 'review_add_review_assignment', - kwargs={'article_id': article.pk} - ) + '?' + parse.urlencode({'user': new_reviewer_form.data['email'], 'id': str(user.pk)},) + "review_add_review_assignment", + kwargs={"article_id": article.pk}, + ) + + "?" + + parse.urlencode( + {"user": new_reviewer_form.data["email"], "id": str(user.pk)}, + ) ) valid = new_reviewer_form.is_valid() @@ -1177,11 +1246,16 @@ def add_review_assignment(request, article_id): acc = logic.handle_reviewer_form(request, new_reviewer_form) return redirect( reverse( - 'review_add_review_assignment', kwargs={'article_id': article.pk} - ) + '?' + parse.urlencode({'user': new_reviewer_form.data['email'], 'id': str(acc.pk)}), + "review_add_review_assignment", + kwargs={"article_id": article.pk}, + ) + + "?" + + parse.urlencode( + {"user": new_reviewer_form.data["email"], "id": str(acc.pk)} + ), ) else: - form.modal = {'id': 'reviewer'} + form.modal = {"id": "reviewer"} else: form = forms.ReviewAssignmentForm( request.POST, @@ -1195,27 +1269,34 @@ def add_review_assignment(request, article_id): article.stage = submission_models.STAGE_UNDER_REVIEW article.save() - kwargs = {'user_message_content': '', - 'review_assignment': review_assignment, - 'request': request, - 'skip': False, - 'acknowledgement': False} + kwargs = { + "user_message_content": "", + "review_assignment": review_assignment, + "request": request, + "skip": False, + "acknowledgement": False, + } - event_logic.Events.raise_event(event_logic.Events.ON_REVIEWER_REQUESTED, **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_REVIEWER_REQUESTED, **kwargs + ) return redirect( reverse( - 'review_notify_reviewer', - kwargs={'article_id': article_id, 'review_id': review_assignment.id} + "review_notify_reviewer", + kwargs={ + "article_id": article_id, + "review_id": review_assignment.id, + }, ) ) - template = 'admin/review/add_review_assignment.html' + template = "admin/review/add_review_assignment.html" context = { - 'article': article, - 'form': form, - 'reviewers': reviewers, - 'new_reviewer_form': new_reviewer_form, + "article": article, + "form": form, + "reviewers": reviewers, + "new_reviewer_form": new_reviewer_form, } return render(request, template, context) @@ -1239,45 +1320,48 @@ def notify_reviewer(request, article_id, review_id): review = get_object_or_404(models.ReviewAssignment, pk=review_id) email_context = logic.get_reviewer_notification_context( - request, article, request.user, review) + request, article, request.user, review + ) form = core_forms.SettingEmailForm( - setting_name="review_assignment", - email_context=email_context, - request=request, + setting_name="review_assignment", + email_context=email_context, + request=request, ) if request.POST: skip = request.POST.get("skip") form = core_forms.SettingEmailForm( - request.POST, request.FILES, - setting_name="review_assignment", - email_context=email_context, - request=request, + request.POST, + request.FILES, + setting_name="review_assignment", + email_context=email_context, + request=request, ) if form.is_valid() or skip: kwargs = { - 'email_data': form.as_dataclass(), - 'review_assignment': review, - 'request': request, - 'skip': skip, + "email_data": form.as_dataclass(), + "review_assignment": review, + "request": request, + "skip": skip, } event_logic.Events.raise_event( - event_logic.Events.ON_REVIEWER_REQUESTED_NOTIFICATION, **kwargs) + event_logic.Events.ON_REVIEWER_REQUESTED_NOTIFICATION, **kwargs + ) review.date_requested = timezone.now() review.save() - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) - template = 'review/notify_reviewer.html' + template = "review/notify_reviewer.html" context = { - 'article': article, - 'review': review, - 'form': form, - 'assignment': review, + "article": article, + "review": review, + "form": form, + "assignment": review, } return render(request, template, context) @@ -1309,7 +1393,7 @@ def view_review(request, article_id, review_id): if request.POST: fire_redirect, extend_answer_accordion = False, False - if 'visibility' in request.POST: + if "visibility" in request.POST: visibility_form = forms.ReviewVisibilityForm( request.POST, instance=review, @@ -1319,11 +1403,11 @@ def view_review(request, article_id, review_id): messages.add_message( request, messages.SUCCESS, - 'Review visibility has been updated.', + "Review visibility has been updated.", ) fire_redirect = True - if 'answer_visibility' in request.POST: + if "answer_visibility" in request.POST: answer_visibility_form = forms.AnswerVisibilityForm( request.POST, review_assignment=review, @@ -1333,13 +1417,13 @@ def view_review(request, article_id, review_id): messages.add_message( request, messages.SUCCESS, - 'Review answer visibility has been updated.', + "Review answer visibility has been updated.", ) extend_answer_accordion = True fire_redirect = True - if 'reset' in request.POST: - answer_pk = request.POST.get('pk') + if "reset" in request.POST: + answer_pk = request.POST.get("pk") answer = models.ReviewAssignmentAnswer.objects.get( assignment=review, pk=answer_pk, @@ -1349,21 +1433,25 @@ def view_review(request, article_id, review_id): fire_redirect = True if fire_redirect: - return redirect("{}{}".format( - reverse( - 'review_view_review', - kwargs={'article_id': article.pk, 'review_id': review.pk, } + return redirect( + "{}{}".format( + reverse( + "review_view_review", + kwargs={ + "article_id": article.pk, + "review_id": review.pk, + }, + ), + "?answer_accordion=True" if extend_answer_accordion else "", ), - "?answer_accordion=True" if extend_answer_accordion else "", - ), ) - template = 'review/view_review.html' + template = "review/view_review.html" context = { - 'article': article, - 'review': review, - 'visibility_form': visibility_form, - 'answer_visibility_form': answer_visibility_form, + "article": article, + "review": review, + "visibility_form": visibility_form, + "answer_visibility_form": answer_visibility_form, } return render(request, template, context) @@ -1400,17 +1488,17 @@ def edit_review_answer(request, article_id, review_id, answer_id): return redirect( reverse( - 'review_view_review', - kwargs={'article_id': article.pk, 'review_id': review.pk}, + "review_view_review", + kwargs={"article_id": article.pk, "review_id": review.pk}, ) ) - template = 'review/edit_review_answer.html' + template = "review/edit_review_answer.html" context = { - 'article': article, - 'review': review, - 'answer': answer, - 'form': form, + "article": article, + "review": review, + "answer": answer, + "form": form, } return render(request, template, context) @@ -1435,26 +1523,40 @@ def edit_review(request, article_id, review_id): review = get_object_or_404(models.ReviewAssignment, pk=review_id) if review.date_complete: - messages.add_message(request, messages.WARNING, 'You cannot edit a review that is already complete.') - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + messages.add_message( + request, + messages.WARNING, + "You cannot edit a review that is already complete.", + ) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) form = forms.EditReviewAssignmentForm(instance=review, journal=request.journal) if request.POST: - form = forms.EditReviewAssignmentForm(request.POST, instance=review, journal=request.journal) + form = forms.EditReviewAssignmentForm( + request.POST, instance=review, journal=request.journal + ) if form.is_valid(): form.save() - messages.add_message(request, messages.INFO, 'Review updates.') - util_models.LogEntry.add_entry('Review Deleted', 'Review updated.', level='Info', actor=request.user, - request=request, target=review) - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + messages.add_message(request, messages.INFO, "Review updates.") + util_models.LogEntry.add_entry( + "Review Deleted", + "Review updated.", + level="Info", + actor=request.user, + request=request, + target=review, + ) + return redirect( + reverse("review_in_review", kwargs={"article_id": article.pk}) + ) - template = 'review/edit_review.html' + template = "review/edit_review.html" context = { - 'article': article, - 'review': review, - 'form': form, + "article": article, + "review": review, + "form": form, } return render(request, template, context) @@ -1479,28 +1581,42 @@ def delete_review(request, article_id, review_id): review = get_object_or_404(models.ReviewAssignment, pk=review_id) if review.date_complete: - messages.add_message(request, messages.WARNING, 'You cannot delete a review that is already complete.') - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + messages.add_message( + request, + messages.WARNING, + "You cannot delete a review that is already complete.", + ) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) - if request.POST and 'delete' in request.POST: - user_message = request.POST.get('delete_rationale', 'No message supplied by user.') - description = 'Review {0} for article {1} has been deleted by {2}. \n\n{3}'.format( - review.pk, - article.title, - request.user.username, - user_message, + if request.POST and "delete" in request.POST: + user_message = request.POST.get( + "delete_rationale", "No message supplied by user." + ) + description = ( + "Review {0} for article {1} has been deleted by {2}. \n\n{3}".format( + review.pk, + article.title, + request.user.username, + user_message, + ) + ) + util_models.LogEntry.add_entry( + "Review Deleted", + description, + level="Info", + actor=request.user, + request=request, + target=article, ) - util_models.LogEntry.add_entry('Review Deleted', description, level='Info', actor=request.user, - request=request, target=article) review.delete() - messages.add_message(request, messages.SUCCESS, 'Review deleted.') - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + messages.add_message(request, messages.SUCCESS, "Review deleted.") + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) - template = 'review/delete_review.html' + template = "review/delete_review.html" context = { - 'article': article, - 'review': review, + "article": article, + "review": review, } return render(request, template, context) @@ -1528,35 +1644,33 @@ def withdraw_review(request, article_id, review_id): messages.add_message( request, messages.WARNING, - 'You cannot withdraw a review that is already complete.', + "You cannot withdraw a review that is already complete.", ) return redirect( reverse( - 'review_in_review', - kwargs={'article_id': article.pk}, + "review_in_review", + kwargs={"article_id": article.pk}, ) ) email_context = logic.get_withdrawal_notification_context(request, review) setting_name = "review_withdrawl" form = core_forms.SettingEmailForm( - setting_name=setting_name, - email_context=email_context, - request=request, + setting_name=setting_name, + email_context=email_context, + request=request, ) if request.POST: - - skip = request.POST.get("skip") form = core_forms.SettingEmailForm( - request.POST, request.FILES, + request.POST, + request.FILES, setting_name=setting_name, email_context=email_context, request=request, ) - if form.is_valid() or skip: review.date_complete = timezone.now() review.decision = models.RD.DECISION_WITHDRAWN.value @@ -1564,30 +1678,29 @@ def withdraw_review(request, article_id, review_id): review.save() kwargs = { - 'review_assignment': review, - 'request': request, - 'email_data': form.as_dataclass(), - 'skip': skip, + "review_assignment": review, + "request": request, + "email_data": form.as_dataclass(), + "skip": skip, } event_logic.Events.raise_event( event_logic.Events.ON_REVIEW_WITHDRAWL, **kwargs, ) - - messages.add_message(request, messages.SUCCESS, 'Review withdrawn') + messages.add_message(request, messages.SUCCESS, "Review withdrawn") return redirect( reverse( - 'review_in_review', - kwargs={'article_id': article.pk}, + "review_in_review", + kwargs={"article_id": article.pk}, ) ) - template = 'review/withdraw_review.html' + template = "review/withdraw_review.html" context = { - 'article': article, - 'review': review, - 'form': form, + "article": article, + "review": review, + "form": form, } return render(request, template, context) @@ -1619,13 +1732,13 @@ def reset_review(request, article_id, review_id): review.suggested_reviewers = "" review.save() - messages.add_message(request, messages.INFO, 'Review reset.') - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + messages.add_message(request, messages.INFO, "Review reset.") + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) - template = 'review/reset.html' + template = "review/reset.html" context = { - 'article': article, - 'review': review, + "article": article, + "review": review, } return render(request, template, context) @@ -1648,117 +1761,128 @@ def review_decision(request, article_id, decision): journal=request.journal, ) author_review_url = request.journal.site_url( - reverse('review_author_view', kwargs={'article_id': article.id}) + reverse("review_author_view", kwargs={"article_id": article.id}) ) email_context = logic.get_decision_context( - request, article, decision, author_review_url) + request, article, decision, author_review_url + ) setting_name = "review_decision_{0}".format(decision) form = core_forms.SettingEmailForm( - setting_name=setting_name, - email_context=email_context, - request=request, + setting_name=setting_name, + email_context=email_context, + request=request, ) - if (article.date_accepted or article.date_declined) and decision != 'undecline': - messages.add_message(request, messages.WARNING, _('This article has already been accepted or declined.')) - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + if (article.date_accepted or article.date_declined) and decision != "undecline": + messages.add_message( + request, + messages.WARNING, + _("This article has already been accepted or declined."), + ) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) if request.POST: form = core_forms.SettingEmailForm( - request.POST, request.FILES, + request.POST, + request.FILES, setting_name=setting_name, email_context=email_context, request=request, ) skip = "skip" in request.POST if form.is_valid() or skip: - kwargs = { - 'article': article, - 'request': request, - 'decision': decision, - 'email_data': form.as_dataclass(), - 'skip': skip, + "article": article, + "request": request, + "decision": decision, + "email_data": form.as_dataclass(), + "skip": skip, } - if decision == 'accept': + if decision == "accept": article.accept_article() article.snapshot_authors(article, force_update=False) try: event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_ACCEPTED, task_object=article, - **kwargs + **kwargs, ) workflow_kwargs = { - 'handshake_url': 'review_home', - 'request': request, - 'article': article, - 'switch_stage': True + "handshake_url": "review_home", + "request": request, + "article": article, + "switch_stage": True, } return event_logic.Events.raise_event( event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, task_object=article, - **workflow_kwargs + **workflow_kwargs, ) except: messages.add_message( request, messages.ERROR, - f'An error occurred when processing {article.title}' + f"An error occurred when processing {article.title}", + ) + return redirect( + reverse("review_in_review", kwargs={"article_id": article.pk}) ) - return redirect(reverse( - 'review_in_review', - kwargs={'article_id': article.pk} - )) - elif decision == 'decline': + elif decision == "decline": article.decline_article() event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_DECLINED, - task_object=article, **kwargs, + task_object=article, + **kwargs, ) - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) - elif decision == 'undecline': + elif decision == "undecline": article.undo_review_decision() event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_UNDECLINED, - task_object=article, **kwargs, + task_object=article, + **kwargs, ) if article.stage == submission_models.STAGE_UNASSIGNED: - return redirect(reverse( - 'review_unassigned_article', - kwargs={'article_id': article.pk}, - )) + return redirect( + reverse( + "review_unassigned_article", + kwargs={"article_id": article.pk}, + ) + ) elif article.stage == submission_models.STAGE_ASSIGNED: - return redirect(reverse( - 'review_in_review', kwargs={'article_id': article.pk})) + return redirect( + reverse("review_in_review", kwargs={"article_id": article.pk}) + ) messages.add_message( - request, messages.INFO, - 'Article {0} has been {1}ed'.format(article.title, decision), + request, + messages.INFO, + "Article {0} has been {1}ed".format(article.title, decision), + ) + return redirect( + reverse("article_copyediting", kwargs={"article_id": article.pk}) ) - return redirect(reverse( - 'article_copyediting', kwargs={'article_id': article.pk})) accept_article_warning = core_logic.render_nested_setting( - 'accept_article_warning', - 'general', + "accept_article_warning", + "general", request, article=article, ) - template = 'review/decision.html' + template = "review/decision.html" context = { - 'article': article, - 'decision': decision, - 'form': form, - 'accept_article_warning': accept_article_warning, + "article": article, + "decision": decision, + "form": form, + "accept_article_warning": accept_article_warning, } return render(request, template, context) @@ -1782,30 +1906,50 @@ def rate_reviewer(request, article_id, review_id): article__journal=request.journal, ) if not review.is_complete: - messages.add_message(request, messages.INFO, 'You cannot rate a reviewer until their review is complete.' - 'You should withdraw this review if you want to rate the reviewer' - 'before they are finished.') - return redirect(reverse('review_in_review', kwargs={'article_id': review.article.id})) + messages.add_message( + request, + messages.INFO, + "You cannot rate a reviewer until their review is complete." + "You should withdraw this review if you want to rate the reviewer" + "before they are finished.", + ) + return redirect( + reverse("review_in_review", kwargs={"article_id": review.article.id}) + ) if request.POST: - rating_int = int(request.POST.get('rating_number')) + rating_int = int(request.POST.get("rating_number")) if review.review_rating: rating = review.review_rating rating.rating = rating_int rating.save() - messages.add_message(request, messages.INFO, - '{0}\'s rating updated to {1}'.format(review.reviewer.full_name(), rating_int)) + messages.add_message( + request, + messages.INFO, + "{0}'s rating updated to {1}".format( + review.reviewer.full_name(), rating_int + ), + ) else: - messages.add_message(request, messages.INFO, - '{0} assigned a rating of {1}'.format(review.reviewer.full_name(), rating_int)) - models.ReviewerRating.objects.create(assignment=review, rating=rating_int, rater=request.user) + messages.add_message( + request, + messages.INFO, + "{0} assigned a rating of {1}".format( + review.reviewer.full_name(), rating_int + ), + ) + models.ReviewerRating.objects.create( + assignment=review, rating=rating_int, rater=request.user + ) - return redirect(reverse('review_in_review', kwargs={'article_id': review.article.id})) + return redirect( + reverse("review_in_review", kwargs={"article_id": review.article.id}) + ) - template = 'review/rate_reviewer.html' + template = "review/rate_reviewer.html" context = { - 'review': review, + "review": review, } return render(request, template, context) @@ -1828,24 +1972,24 @@ def author_view_reviews(request, article_id): article=article, is_complete=True, for_author_consumption=True, - ).exclude(decision='withdrawn') + ).exclude(decision="withdrawn") if not reviews.exists(): raise PermissionDenied( - 'No reviews have been made available by the Editor.', + "No reviews have been made available by the Editor.", ) - if request.GET.get('file_id', None): + if request.GET.get("file_id", None): viewable_files = logic.group_files(article, reviews) - file_id = request.GET.get('file_id') + file_id = request.GET.get("file_id") file = get_object_or_404(core_models.File, pk=file_id) if file in viewable_files: return files.serve_file(request, file, article) - template = 'review/author_view_reviews.html' + template = "review/author_view_reviews.html" context = { - 'article': article, - 'reviews': reviews, + "article": article, + "reviews": reviews, } return render(request, template, context) @@ -1886,20 +2030,22 @@ def request_revisions(request, article_id): article.stage = submission_models.STAGE_UNDER_REVISION article.save() - return redirect(reverse( - 'request_revisions_notification', - kwargs={ - 'article_id': article.pk, - 'revision_id': revision_request.pk, - } - )) + return redirect( + reverse( + "request_revisions_notification", + kwargs={ + "article_id": article.pk, + "revision_id": revision_request.pk, + }, + ) + ) - template = 'review/revision/request_revisions.html' + template = "review/revision/request_revisions.html" context = { - 'article': article, - 'form': form, - 'pending_approval': pending_approval, - 'incomplete': incomplete, + "article": article, + "form": form, + "pending_approval": pending_approval, + "incomplete": incomplete, } return render(request, template, context) @@ -1924,27 +2070,29 @@ def request_revisions_notification(request, article_id, revision_id): email_content = logic.get_revision_request_content(request, article, revision) if request.POST: - user_message_content = request.POST.get('email_content') + user_message_content = request.POST.get("email_content") kwargs = { - 'user_message_content': user_message_content, - 'revision': revision, - 'request': request, - 'skip': False, + "user_message_content": user_message_content, + "revision": revision, + "request": request, + "skip": False, } - if 'skip' in request.POST: - kwargs['skip'] = True + if "skip" in request.POST: + kwargs["skip"] = True - event_logic.Events.raise_event(event_logic.Events.ON_REVISIONS_REQUESTED_NOTIFY, **kwargs) + event_logic.Events.raise_event( + event_logic.Events.ON_REVISIONS_REQUESTED_NOTIFY, **kwargs + ) - return redirect(reverse('review_in_review', kwargs={'article_id': article.pk})) + return redirect(reverse("review_in_review", kwargs={"article_id": article.pk})) - template = 'review/revision/request_revisions_notification.html' + template = "review/revision/request_revisions_notification.html" context = { - 'article': article, - 'email_content': email_content, - 'revision': revision, + "article": article, + "email_content": email_content, + "revision": revision, } return render(request, template, context) @@ -1964,46 +2112,62 @@ def edit_revision_request(request, article_id, revision_id): models.RevisionRequest, article__pk=article_id, article__journal=request.journal, - pk=revision_id + pk=revision_id, ) form = forms.EditRevisionDue(instance=revision_request) if revision_request.date_completed: - messages.add_message(request, messages.WARNING, 'You cannot edit a revision request that is complete.') - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + messages.add_message( + request, + messages.WARNING, + "You cannot edit a revision request that is complete.", + ) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) if request.POST: - - if 'update_due' in request.POST: + if "update_due" in request.POST: form = forms.EditRevisionDue(request.POST, instance=revision_request) if form.is_valid(): form.save() - messages.add_message(request, messages.INFO, 'Due date updated.') + messages.add_message(request, messages.INFO, "Due date updated.") - if 'delete_revision' in request.POST: - rationale = request.POST.get('delete_rationale') - util_models.LogEntry.add_entry('deletion', '{0} deleted a revision request with reason:\n\n{1}'.format( - request.user.full_name(), rationale), level='Info', actor=request.user, target=revision_request.article + if "delete_revision" in request.POST: + rationale = request.POST.get("delete_rationale") + util_models.LogEntry.add_entry( + "deletion", + "{0} deleted a revision request with reason:\n\n{1}".format( + request.user.full_name(), rationale + ), + level="Info", + actor=request.user, + target=revision_request.article, ) revision_request.delete() - messages.add_message(request, messages.INFO, 'Revision request deleted.') + messages.add_message(request, messages.INFO, "Revision request deleted.") - if 'mark_as_complete' in request.POST: - util_models.LogEntry.add_entry('update', '{0} marked revision {1} as complete'.format( - request.user.full_name(), revision_request.id), level='Info', actor=request.user, - target=revision_request.article + if "mark_as_complete" in request.POST: + util_models.LogEntry.add_entry( + "update", + "{0} marked revision {1} as complete".format( + request.user.full_name(), revision_request.id + ), + level="Info", + actor=request.user, + target=revision_request.article, ) revision_request.date_completed = timezone.now() revision_request.save() - messages.add_message(request, messages.INFO, 'Revision request marked as complete.') + messages.add_message( + request, messages.INFO, "Revision request marked as complete." + ) - return redirect(reverse('review_in_review', kwargs={'article_id': article_id})) + return redirect(reverse("review_in_review", kwargs={"article_id": article_id})) - template = 'review/revision/edit_revision_request.html' + template = "review/revision/edit_revision_request.html" context = { - 'revision_request': revision_request, - 'form': form, + "revision_request": revision_request, + "form": form, } return render(request, template, context) @@ -2031,44 +2195,35 @@ def do_revisions(request, article_id, revision_id): article=revision_request.article, is_complete=True, for_author_consumption=True, - ).exclude(decision='withdrawn') + ).exclude(decision="withdrawn") form = forms.DoRevisions(instance=revision_request) revision_files = logic.group_files(revision_request.article, reviews) if request.POST: post_redirect = reverse( - 'do_revisions', - kwargs={ - 'article_id': article_id, - 'revision_id': revision_id - } + "do_revisions", + kwargs={"article_id": article_id, "revision_id": revision_id}, ) - if 'delete' in request.POST: - file_id = request.POST.get('delete') + if "delete" in request.POST: + file_id = request.POST.get("delete") file = get_object_or_404(core_models.File, pk=file_id) files.delete_file(revision_request.article, file) logic.log_revision_event( - 'File {0} ({1}) deleted.'.format( - file.id, - file.original_filename - ), + "File {0} ({1}) deleted.".format(file.id, file.original_filename), request.user, revision_request, ) return redirect(post_redirect) - elif 'save' in request.POST: - form = forms.DoRevisions( - request.POST, - instance=revision_request - ) + elif "save" in request.POST: + form = forms.DoRevisions(request.POST, instance=revision_request) if form.is_valid(): form.save() messages.add_message( request, messages.SUCCESS, - 'Thanks. Your covering letter has been saved.', + "Thanks. Your covering letter has been saved.", ) return redirect(post_redirect) else: @@ -2076,47 +2231,44 @@ def do_revisions(request, article_id, revision_id): if not revision_request.article.has_manuscript_file(): form.add_error( None, - 'Your article must have at least one manuscript file.', + "Your article must have at least one manuscript file.", ) if form.is_valid() and form.is_confirmed(): form.save() kwargs = { - 'revision': revision_request, - 'request': request, + "revision": revision_request, + "request": request, } event_logic.Events.raise_event( - event_logic.Events.ON_REVISIONS_COMPLETE, - **kwargs + event_logic.Events.ON_REVISIONS_COMPLETE, **kwargs ) messages.add_message( request, messages.SUCCESS, - 'Thank you for submitting your revisions. ' - 'The Editor has been notified.', + "Thank you for submitting your revisions. " + "The Editor has been notified.", ) revision_request.date_completed = timezone.now() revision_request.save() - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) - if request.GET.get('file_id', None): - file_id = request.GET.get('file_id') + if request.GET.get("file_id", None): + file_id = request.GET.get("file_id") file = get_object_or_404(core_models.File, pk=file_id) if file in revision_files: logic.log_revision_event( - 'Downloaded file {0} ({1}).'.format( - file.label, - file.original_filename), + "Downloaded file {0} ({1}).".format(file.label, file.original_filename), request.user, revision_request, ) return files.serve_file(request, file, revision_request.article) - template = 'admin/review/revision/do_revision.html' + template = "admin/review/revision/do_revision.html" context = { - 'revision_request': revision_request, - 'form': form, - 'article': revision_request.article, - 'reviews': reviews, + "revision_request": revision_request, + "form": form, + "article": revision_request.article, + "reviews": reviews, } return render(request, template, context) @@ -2133,18 +2285,26 @@ def replace_file(request, article_id, revision_id, file_id): ) file = get_object_or_404(core_models.File, pk=file_id) - if request.GET.get('download', None): - logic.log_revision_event('Downloaded file {0} ({1})'.format(file.label, file.original_filename), request.user, - revision_request) + if request.GET.get("download", None): + logic.log_revision_event( + "Downloaded file {0} ({1})".format(file.label, file.original_filename), + request.user, + revision_request, + ) return files.serve_file(request, file, revision_request.article) if request.POST and request.FILES: - - if 'replacement' in request.POST: - uploaded_file = request.FILES.get('replacement-file') - label = request.POST.get('label') - new_file = files.save_file_to_article(uploaded_file, revision_request.article, request.user, - replace=file, is_galley=False, label=label) + if "replacement" in request.POST: + uploaded_file = request.FILES.get("replacement-file") + label = request.POST.get("label") + new_file = files.save_file_to_article( + uploaded_file, + revision_request.article, + request.user, + replace=file, + is_galley=False, + label=label, + ) files.replace_file( revision_request.article, @@ -2153,17 +2313,28 @@ def replace_file(request, article_id, revision_id, file_id): retain_old_label=False, ) logic.log_revision_event( - 'File {0} ({1}) replaced with {2} ({3})'.format(file.label, file.original_filename, new_file.label, - new_file.original_filename), - request.user, revision_request) + "File {0} ({1}) replaced with {2} ({3})".format( + file.label, + file.original_filename, + new_file.label, + new_file.original_filename, + ), + request.user, + revision_request, + ) - return redirect(reverse('do_revisions', kwargs={'article_id': article_id, 'revision_id': revision_id})) + return redirect( + reverse( + "do_revisions", + kwargs={"article_id": article_id, "revision_id": revision_id}, + ) + ) - template = 'review/revision/replace_file.html' + template = "review/revision/replace_file.html" context = { - 'revision_request': revision_request, - 'article': revision_request.article, - 'file': file, + "revision_request": revision_request, + "article": revision_request.article, + "file": file, } return render(request, template, context) @@ -2188,9 +2359,9 @@ def upload_new_file(request, article_id, revision_id): article = revision_request.article if request.POST and request.FILES: - file_type = request.POST.get('file_type') - uploaded_file = request.FILES.get('file') - label = request.POST.get('label') + file_type = request.POST.get("file_type") + uploaded_file = request.FILES.get("file") + label = request.POST.get("label") new_file = files.save_file_to_article( uploaded_file, article, @@ -2198,26 +2369,31 @@ def upload_new_file(request, article_id, revision_id): label=label, ) - if file_type == 'manuscript': + if file_type == "manuscript": article.manuscript_files.add(new_file) - if file_type == 'data': + if file_type == "data": article.data_figure_files.add(new_file) logic.log_revision_event( - 'New file {0} ({1}) uploaded'.format( - new_file.label, new_file.original_filename), - request.user, revision_request) + "New file {0} ({1}) uploaded".format( + new_file.label, new_file.original_filename + ), + request.user, + revision_request, + ) - return redirect(reverse( - 'do_revisions', - kwargs={'article_id': article_id, 'revision_id': revision_id}) + return redirect( + reverse( + "do_revisions", + kwargs={"article_id": article_id, "revision_id": revision_id}, + ) ) - template = 'review/revision/upload_file.html' + template = "review/revision/upload_file.html" context = { - 'revision_request': revision_request, - 'article': revision_request.article, + "revision_request": revision_request, + "article": revision_request.article, } return render(request, template, context) @@ -2234,16 +2410,16 @@ def view_revision(request, article_id, revision_id): :return: HttpResponse """ revision_request = get_object_or_404( - models.RevisionRequest.objects.select_related('article'), + models.RevisionRequest.objects.select_related("article"), pk=revision_id, article__pk=article_id, article__journal=request.journal, ) - template = 'review/revision/view_revision.html' + template = "review/revision/view_revision.html" context = { - 'revision_request': revision_request, - 'article': revision_request.article + "revision_request": revision_request, + "article": revision_request.article, } return render(request, template, context) @@ -2267,28 +2443,29 @@ def review_warning(request, article_id, decision): if request.POST and request.user.is_editor(request): override = models.EditorOverride.objects.create( - article=article, editor=request.user) - kwargs = {'request': request, 'override': override} + article=article, editor=request.user + ) + kwargs = {"request": request, "override": override} event_logic.Events.raise_event( - event_logic.Events.ON_REVIEW_SECURITY_OVERRIDE, - task_object=article, - **kwargs + event_logic.Events.ON_REVIEW_SECURITY_OVERRIDE, + task_object=article, + **kwargs, ) if decision == ED.REVIEW.value: return redirect( reverse( - 'review_in_review', - kwargs={'article_id': article.pk}, + "review_in_review", + kwargs={"article_id": article.pk}, ), ) else: return redirect( reverse( - 'review_decision', + "review_decision", kwargs={ - 'article_id': article.pk, - 'decision': decision, + "article_id": article.pk, + "decision": decision, }, ), ) @@ -2296,13 +2473,11 @@ def review_warning(request, article_id, decision): messages.add_message( request, messages.WARNING, - 'This action is not allowed.', + "This action is not allowed.", ) - template = 'review/review_warning.html' - context = { - 'article': article - } + template = "review/review_warning.html" + context = {"article": article} return render(request, template, context) @@ -2310,7 +2485,7 @@ def review_warning(request, article_id, decision): @editor_user_required @file_user_required def editor_article_file(request, article_id, file_id): - """ Serves an article file. + """Serves an article file. :param request: the request associated with this call :param article_id: the id of an article @@ -2328,7 +2503,7 @@ def editor_article_file(request, article_id, file_id): @reviewer_user_for_assignment_required def reviewer_article_file(request, assignment_id, file_id): - """ Serves an article file. + """Serves an article file. :param request: the request associated with this call :param assignment_id: the ReviewAssignment id. :param file_id: the file ID to serve @@ -2342,12 +2517,7 @@ def reviewer_article_file(request, assignment_id, file_id): if not file_object: raise Http404() - return files.serve_file( - request, - file_object, - article_object, - hide_name=True - ) + return files.serve_file(request, file_object, article_object, hide_name=True) @reviewer_user_for_assignment_required @@ -2384,20 +2554,21 @@ def draft_decision(request, article_id): message_to_editor=message_to_editor, editors=editors, initial={ - 'revision_request_due_date': timezone.now() + timedelta(days=14), - } + "revision_request_due_date": timezone.now() + timedelta(days=14), + }, ) if request.POST: - - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - draft = get_object_or_404(models.DecisionDraft, pk=delete_id, article=article) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + draft = get_object_or_404( + models.DecisionDraft, pk=delete_id, article=article + ) draft.delete() return redirect( reverse( - 'review_draft_decision', - kwargs={'article_id': article.pk}, + "review_draft_decision", + kwargs={"article_id": article.pk}, ), ) @@ -2417,10 +2588,10 @@ def draft_decision(request, article_id): messages.add_message( request, messages.SUCCESS, - 'A draft has been saved, the editor has been notified.', + "A draft has been saved, the editor has been notified.", ) - kwargs = {'request': request, 'article': article, 'draft': new_draft} + kwargs = {"request": request, "article": article, "draft": new_draft} event_logic.Events.raise_event( event_logic.Events.ON_DRAFT_DECISION, **kwargs, @@ -2428,16 +2599,16 @@ def draft_decision(request, article_id): return redirect( reverse( - 'review_draft_decision', - kwargs={'article_id': article.pk}, + "review_draft_decision", + kwargs={"article_id": article.pk}, ), ) - template = 'review/draft_decision.html' + template = "review/draft_decision.html" context = { - 'article': article, - 'drafts': drafts, - 'form': form, + "article": article, + "drafts": drafts, + "form": form, } return render(request, template, context) @@ -2454,18 +2625,18 @@ def draft_decision_text(request, article_id): pk=article_id, journal=request.journal, ) - decision = request.POST.get('decision') - date = request.POST.get('date', None) + decision = request.POST.get("decision") + date = request.POST.get("date", None) - if isinstance(date, str) and date != '': - date = shared.make_timezone_aware(date, '%Y-%m-%d') + if isinstance(date, str) and date != "": + date = shared.make_timezone_aware(date, "%Y-%m-%d") else: date = timezone.now() + timedelta(days=14) author_review_url = request.journal.site_url( reverse( - 'review_author_view', - kwargs={'article_id': article.id}, + "review_author_view", + kwargs={"article_id": article.id}, ) ) @@ -2496,7 +2667,7 @@ def draft_decision_text(request, article_id): draft=True, ) - return JsonResponse({'decision_text': decision_text}) + return JsonResponse({"decision_text": decision_text}) @editor_is_not_author @@ -2509,13 +2680,13 @@ def manage_draft(request, article_id, draft_id): ) draft = get_object_or_404(models.DecisionDraft, pk=draft_id) - if 'decline_draft' in request.POST: - draft.editor_decision = 'declined' + if "decline_draft" in request.POST: + draft.editor_decision = "declined" draft.save() logic.handle_draft_declined(article, draft, request) - if 'accept_draft' in request.POST: - draft.editor_decision = 'accept' + if "accept_draft" in request.POST: + draft.editor_decision = "accept" draft.save() decision_action = logic.handle_decision_action(article, draft, request) @@ -2523,15 +2694,13 @@ def manage_draft(request, article_id, draft_id): return decision_action messages.add_message( - request, - messages.INFO, - 'Draft {}'.format(draft.editor_decision) + request, messages.INFO, "Draft {}".format(draft.editor_decision) ) return redirect( reverse( - 'decision_helper', - kwargs={'article_id': article.pk}, + "decision_helper", + kwargs={"article_id": article.pk}, ), ) @@ -2561,20 +2730,20 @@ def edit_draft_decision(request, article_id, draft_id): if form.is_valid(): form.save() - messages.add_message(request, messages.SUCCESS, 'Draft has been updated') + messages.add_message(request, messages.SUCCESS, "Draft has been updated") return redirect( reverse( - 'review_edit_draft_decision', - kwargs={'article_id': article.pk, 'draft_id': draft.pk}, + "review_edit_draft_decision", + kwargs={"article_id": article.pk, "draft_id": draft.pk}, ), ) - template = 'review/draft_decision.html' + template = "review/draft_decision.html" context = { - 'article': article, - 'drafts': drafts, - 'draft': draft, - 'form': form, + "article": article, + "drafts": drafts, + "draft": draft, + "form": form, } return render(request, template, context) @@ -2594,13 +2763,15 @@ def review_forms(request): form = forms.NewForm() default_form = setting_handler.get_setting( - 'general', 'default_review_form', request.journal, + "general", + "default_review_form", + request.journal, ).processed_value if default_form and default_form.isdigit(): default_form = int(default_form) if request.POST: - if 'delete' in request.POST: + if "delete" in request.POST: form_id = request.POST["delete"] if form_id.isdigit(): form_id = int(form_id) @@ -2611,16 +2782,17 @@ def review_forms(request): "This form is selected as the default form and thus" " can't be deleted", ) - return redirect(reverse('review_review_forms')) + return redirect(reverse("review_review_forms")) form_obj = get_object_or_404( - models.ReviewForm, id=form_id, + models.ReviewForm, + id=form_id, journal=request.journal, ) form_obj.deleted = True form_obj.save() - messages.add_message(request, messages.SUCCESS, 'Form Deleted') - return redirect(reverse('review_review_forms')) + messages.add_message(request, messages.SUCCESS, "Form Deleted") + return redirect(reverse("review_review_forms")) else: form = forms.NewForm(request.POST) @@ -2629,13 +2801,13 @@ def review_forms(request): new_form.journal = request.journal new_form.save() - return redirect(reverse('review_review_forms')) + return redirect(reverse("review_review_forms")) - template = 'review/manager/review_forms.html' + template = "review/manager/review_forms.html" context = { - 'form_list': form_list, - 'form': form, - 'default_form': default_form, + "form_list": form_list, + "form": form, + "default_form": default_form, } return render(request, template, context) @@ -2658,18 +2830,21 @@ def edit_review_form(request, form_id, element_id=None): if element_id: element = get_object_or_404(models.ReviewFormElement, pk=element_id) - modal = 'element' + modal = "element" element_form = forms.ElementForm(instance=element) if request.POST: - - if 'delete' in request.POST: - delete_id = request.POST.get('delete') - element_to_delete = get_object_or_404(models.ReviewFormElement, pk=delete_id) + if "delete" in request.POST: + delete_id = request.POST.get("delete") + element_to_delete = get_object_or_404( + models.ReviewFormElement, pk=delete_id + ) element_to_delete.delete() - return redirect(reverse('edit_review_form', kwargs={'form_id': edit_form.pk})) + return redirect( + reverse("edit_review_form", kwargs={"form_id": edit_form.pk}) + ) - if 'element' in request.POST: + if "element" in request.POST: if element_id: element_form = forms.ElementForm(request.POST, instance=element) else: @@ -2678,23 +2853,27 @@ def edit_review_form(request, form_id, element_id=None): if element_form.is_valid(): element = element_form.save() edit_form.elements.add(element) - messages.add_message(request, messages.SUCCESS, 'New element added.') - return redirect(reverse('edit_review_form', kwargs={'form_id': edit_form.pk})) + messages.add_message(request, messages.SUCCESS, "New element added.") + return redirect( + reverse("edit_review_form", kwargs={"form_id": edit_form.pk}) + ) - if 'review_form' in request.POST: + if "review_form" in request.POST: form = forms.NewForm(request.POST, instance=edit_form) if form.is_valid(): form.save() - messages.add_message(request, messages.SUCCESS, 'Form updated') - return redirect(reverse('edit_review_form', kwargs={'form_id': edit_form.pk})) + messages.add_message(request, messages.SUCCESS, "Form updated") + return redirect( + reverse("edit_review_form", kwargs={"form_id": edit_form.pk}) + ) - template = 'review/manager/edit_review_form.html' + template = "review/manager/edit_review_form.html" context = { - 'form': form, - 'edit_form': edit_form, - 'element_form': element_form, - 'modal': modal, + "form": form, + "edit_form": edit_form, + "element_form": element_form, + "modal": modal, } return render(request, template, context) @@ -2706,13 +2885,13 @@ def preview_form(request, form_id): form = get_object_or_404(models.ReviewForm, pk=form_id) generated_form = forms.GeneratedForm(preview=form) recommendation_disabled = setting_handler.get_setting( - 'general', - 'disable_reviewer_recommendation', + "general", + "disable_reviewer_recommendation", request.journal, ).processed_value open_peer_review = setting_handler.get_setting( - 'general', - 'open_peer_review', + "general", + "open_peer_review", request.journal, ) @@ -2721,11 +2900,11 @@ def preview_form(request, form_id): open_peer_review=open_peer_review, ) - template = 'review/manager/preview_form.html' + template = "review/manager/preview_form.html" context = { - 'form': form, - 'generated_form': generated_form, - 'decision_form': decision_form, + "form": form, + "generated_form": generated_form, + "decision_form": decision_form, } return render(request, template, context) @@ -2747,11 +2926,11 @@ def order_review_elements(request, form_id): shared.set_order( form.elements.all(), - 'order', - request.POST.getlist('element[]'), + "order", + request.POST.getlist("element[]"), ) - return HttpResponse('Ok') + return HttpResponse("Ok") @reviewer_user_for_assignment_required @@ -2767,29 +2946,29 @@ def hypothesis_review(request, assignment_id): if access_code: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(access_code=access_code) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(access_code=access_code) ) else: assignment = models.ReviewAssignment.objects.get( - Q(pk=assignment_id) & - Q(is_complete=False) & - Q(article__stage=submission_models.STAGE_UNDER_REVIEW) & - Q(reviewer=request.user) + Q(pk=assignment_id) + & Q(is_complete=False) + & Q(article__stage=submission_models.STAGE_UNDER_REVIEW) + & Q(reviewer=request.user) ) - pdf = assignment.review_round.review_files.get(mime_type='application/pdf') + pdf = assignment.review_round.review_files.get(mime_type="application/pdf") hypothesis.create_hypothesis_account(assignment.reviewer) grant_token = hypothesis.generate_grant_token(assignment.reviewer) - template = 'review/annotation_pdf_review.html' + template = "review/annotation_pdf_review.html" context = { - 'assignment': assignment, - 'pdf': pdf, - 'grant_token': grant_token, - 'authority': settings.HYPOTHESIS_CLIENT_AUTHORITY, + "assignment": assignment, + "pdf": pdf, + "grant_token": grant_token, + "authority": settings.HYPOTHESIS_CLIENT_AUTHORITY, } return render(request, template, context) @@ -2823,26 +3002,25 @@ def decision_helper(request, article_id): is_complete=True, date_complete__isnull=False, ).exclude( - decision='withdrawn', + decision="withdrawn", ) withdraw_reviews = reviews.filter( - decision='withdrawn', + decision="withdrawn", ) uncomplete_reviews = uncomplete_reviews.union(withdraw_reviews) decisions = Counter( - [review.get_decision_display() for review in reviews if - review.decision] + [review.get_decision_display() for review in reviews if review.decision] ) if request.POST: - if 'review_id' in request.POST: + if "review_id" in request.POST: review = get_object_or_404( models.ReviewAssignment, - pk=request.POST.get('review_id'), + pk=request.POST.get("review_id"), article=article, ) - if 'visibility' in request.POST: + if "visibility" in request.POST: review.for_author_consumption = True else: review.for_author_consumption = False @@ -2851,36 +3029,42 @@ def decision_helper(request, article_id): messages.add_message( request, messages.SUCCESS, - 'Review {} is now {}'.format( + "Review {} is now {}".format( review.pk, - 'visible to the author.' if review.for_author_consumption else 'hidden from the author.', - ) + "visible to the author." + if review.for_author_consumption + else "hidden from the author.", + ), ) - if 'review_file_visible' in request.POST: + if "review_file_visible" in request.POST: review = get_object_or_404( models.ReviewAssignment, article=article, - id=request.POST.get('review'), + id=request.POST.get("review"), + ) + logic.handle_review_file_switch( + review, request.POST.get("review_file_visible") + ) + messages.add_message( + request, messages.SUCCESS, "Review File visibility updated." ) - logic.handle_review_file_switch(review, request.POST.get('review_file_visible')) - messages.add_message(request, messages.SUCCESS, 'Review File visibility updated.') return redirect( reverse( - 'decision_helper', + "decision_helper", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) - template = 'admin/review/decision_helper.html' + template = "admin/review/decision_helper.html" context = { - 'article': article, - 'complete_reviews': complete_reviews, - 'uncomplete_reviews': uncomplete_reviews, - 'decisions': dict(decisions) + "article": article, + "complete_reviews": complete_reviews, + "uncomplete_reviews": uncomplete_reviews, + "decisions": dict(decisions), } return render(request, template, context) @@ -2901,7 +3085,7 @@ def upload_reviewers_from_csv(request, article_id): journal=request.journal, ) if request.POST and request.FILES: - reviewer_csv = request.FILES.get('reviewer_csv') + reviewer_csv = request.FILES.get("reviewer_csv") form = forms.BulkReviewAssignmentForm( request.POST, request.FILES, @@ -2909,7 +3093,9 @@ def upload_reviewers_from_csv(request, article_id): ) filename, path = files.save_file_to_temp(reviewer_csv) if form.is_valid(): - reviewers, import_error = logic.process_reviewer_csv(path, request, article, form) + reviewers, import_error = logic.process_reviewer_csv( + path, request, article, form + ) if import_error: messages.add_message( @@ -2921,25 +3107,25 @@ def upload_reviewers_from_csv(request, article_id): messages.add_message( request, messages.SUCCESS, - '{} Review assignments saved.'.format(len(reviewers)), + "{} Review assignments saved.".format(len(reviewers)), ) article.stage = submission_models.STAGE_UNDER_REVIEW article.save() return redirect( reverse( - 'review_in_review', + "review_in_review", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) - template = 'admin/review/upload_reviewers_from_csv.html' + template = "admin/review/upload_reviewers_from_csv.html" context = { - 'article': article, - 'form': form, - 'errors': errors, + "article": article, + "form": form, + "errors": errors, } return render( request, @@ -2950,8 +3136,8 @@ def upload_reviewers_from_csv(request, article_id): @editor_user_required @setting_is_enabled( - setting_name='enable_share_reviews_decision', - setting_group_name='general', + setting_name="enable_share_reviews_decision", + setting_group_name="general", ) def editor_share_reviews(request, article_id): """ @@ -2986,30 +3172,28 @@ def editor_share_reviews(request, article_id): request, article, request.journal.get_setting( - 'email_subject', - 'subject_share_reviews_notification', + "email_subject", + "subject_share_reviews_notification", ), form.cleaned_data, ) messages.add_message( - request, - messages.SUCCESS, - 'Reviews shared with reviewers.' + request, messages.SUCCESS, "Reviews shared with reviewers." ) return redirect( reverse( - 'decision_helper', + "decision_helper", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) - template = 'review/share/editor.html' + template = "review/share/editor.html" context = { - 'article': article, - 'reviews': reviews, - 'form': form, + "article": article, + "reviews": reviews, + "form": form, } return render( request, @@ -3020,8 +3204,8 @@ def editor_share_reviews(request, article_id): @user_has_completed_review_for_article @setting_is_enabled( - setting_name='enable_share_reviews_decision', - setting_group_name='general', + setting_name="enable_share_reviews_decision", + setting_group_name="general", ) def reviewer_share_reviews(request, article_id): article = get_object_or_404( @@ -3031,11 +3215,11 @@ def reviewer_share_reviews(request, article_id): reviews_shared=True, ) reviews = article.completed_reviews_with_decision - template = 'review/share/reviewer.html' + template = "review/share/reviewer.html" context = { - 'article': article, - 'reviews': reviews, - 'shared_reviews': True, + "article": article, + "reviews": reviews, + "shared_reviews": True, } return render( request, @@ -3069,19 +3253,20 @@ def reviewer_shared_review_download(request, article_id, review_id): # Route 1 if request.journal.get_setting( - 'general', - 'display_completed_reviews_in_additional_rounds', + "general", + "display_completed_reviews_in_additional_rounds", ): # Fetch all the users who have an active review assignment in the # article's current round. current_round_reviewers = [ - review.reviewer for review in - models.ReviewAssignment.objects.filter( + review.reviewer + for review in models.ReviewAssignment.objects.filter( article=article, review_round=article.current_review_round_object(), date_complete__isnull=True, is_complete=False, - )] + ) + ] # If the current user is in the list, serve the file. if request.user in current_round_reviewers: @@ -3095,8 +3280,7 @@ def reviewer_shared_review_download(request, article_id, review_id): if article.reviews_shared: # Fetch all reviewers who have completed a review. reviewers_with_complete_review = [ - review.reviewer for review in - article.completed_reviews_with_decision + review.reviewer for review in article.completed_reviews_with_decision ] if request.user in reviewers_with_complete_review: return files.serve_file( @@ -3105,6 +3289,4 @@ def reviewer_shared_review_download(request, article_id, review_id): article, ) - raise Http404( - 'You do not have permission to download this file.' - ) + raise Http404("You do not have permission to download this file.") diff --git a/src/rss/apps.py b/src/rss/apps.py index 853dda8300..0329862593 100755 --- a/src/rss/apps.py +++ b/src/rss/apps.py @@ -6,4 +6,4 @@ class RssConfig(AppConfig): - name = 'rss' + name = "rss" diff --git a/src/rss/urls.py b/src/rss/urls.py index 6f105e1940..7ad54bf9a1 100755 --- a/src/rss/urls.py +++ b/src/rss/urls.py @@ -6,8 +6,8 @@ from rss import views urlpatterns = [ - re_path(r'^news/$', views.LatestNewsFeed(), name='rss_news'), - re_path(r'^articles/$', views.LatestArticlesFeed(), name='rss_articles'), - re_path(r'^preprints/$', views.LatestPreprintsFeed(), name='rss_preprints'), - re_path(r'^', views.LatestArticlesFeed(), name='rss_articles'), + re_path(r"^news/$", views.LatestNewsFeed(), name="rss_news"), + re_path(r"^articles/$", views.LatestArticlesFeed(), name="rss_articles"), + re_path(r"^preprints/$", views.LatestPreprintsFeed(), name="rss_preprints"), + re_path(r"^", views.LatestArticlesFeed(), name="rss_articles"), ] diff --git a/src/rss/views.py b/src/rss/views.py index 2718650256..309e10c47e 100755 --- a/src/rss/views.py +++ b/src/rss/views.py @@ -17,6 +17,7 @@ class LatestNewsFeed(Feed): """RSS feed for Journal news articles""" + link = "/news/" def get_object(self, request, *args, **kwargs): @@ -33,7 +34,7 @@ def items(self, obj): return comms_models.NewsItem.objects.filter( content_type=content_type, object_id=obj.pk, - ).order_by('sequence')[:10] + ).order_by("sequence")[:10] def item_title(self, item): return striptags(item.title) @@ -42,7 +43,7 @@ def item_description(self, item): return truncatesmart(item.body, 400) def item_author_name(self, item): - if hasattr(item, 'posted_by'): + if hasattr(item, "posted_by"): return item.posted_by.full_name() else: return None @@ -52,16 +53,17 @@ def item_pubdate(self, item): # item_link is only needed if NewsItem has no get_absolute_url method. def item_link(self, item): - return reverse('core_news_item', args=[item.pk]) + return reverse("core_news_item", args=[item.pk]) class LatestArticlesFeed(Feed): """RSS feed for journal articles""" + link = "/articles/" def get_object(self, request, *args, **kwargs): return request.journal or request.press - + def title(self, obj): return "{} Article Feed".format(obj.name) @@ -71,15 +73,14 @@ def description(self, obj): def items(self, obj): try: return submission_models.Article.objects.filter( - date_published__lte=timezone.now(), - journal=obj - ).order_by('-date_published')[:10] + date_published__lte=timezone.now(), journal=obj + ).order_by("-date_published")[:10] except ValueError: return submission_models.Article.objects.filter( date_published__lte=timezone.now(), journal__press=obj, journal__hide_from_press=False, - ).order_by('-date_published')[:10] + ).order_by("-date_published")[:10] def item_title(self, item): return striptags(item.title) @@ -88,7 +89,7 @@ def item_description(self, item): return truncatesmart(item.abstract, 400) def item_author_name(self, item): - if hasattr(item, 'posted_by'): + if hasattr(item, "posted_by"): return item.correspondence_author.full_name() else: return None @@ -103,8 +104,10 @@ def feed_url(self, obj): def item_link(self, item): return item.url + class LatestPreprintsFeed(Feed): """RSS feed for preprints""" + link = "/preprints/" def get_object(self, request, *args, **kwargs): @@ -118,9 +121,8 @@ def description(self, obj): def items(self, obj): return repo_models.Preprint.objects.filter( - date_published__lte=timezone.now(), - repository=obj - ).order_by('-date_published')[:10] + date_published__lte=timezone.now(), repository=obj + ).order_by("-date_published")[:10] def item_title(self, item): return striptags(item.title) @@ -129,7 +131,7 @@ def item_description(self, item): return truncatesmart(item.abstract, 400) def item_author_name(self, item): - if hasattr(item, 'owner'): + if hasattr(item, "owner"): return item.owner.full_name() else: return None @@ -142,4 +144,4 @@ def feed_url(self, obj): # item_link is only needed if NewsItem has no get_absolute_url method. def item_link(self, item): - return item.url \ No newline at end of file + return item.url diff --git a/src/security/decorators.py b/src/security/decorators.py index 7b53afee47..c4b2127cb5 100755 --- a/src/security/decorators.py +++ b/src/security/decorators.py @@ -34,6 +34,7 @@ # General role-based security decorators + def base_check(request, login_redirect=False): """Janeway equivalent to Django's login_required logic @@ -50,10 +51,10 @@ def base_check(request, login_redirect=False): if login_redirect is True: request_params = request.GET.urlencode() params = urlencode({"next": f"{request.path}?{request_params}"}) - return redirect('{0}?{1}'.format(reverse('core_login'), params)) + return redirect("{0}?{1}".format(reverse("core_login"), params)) elif isinstance(login_redirect, str): params = urlencode({"next": redirect}) - return redirect('{0}?{1}'.format(reverse('core_login'), params)) + return redirect("{0}?{1}".format(reverse("core_login"), params)) else: return False @@ -61,10 +62,11 @@ def base_check(request, login_redirect=False): def base_check_required(func): - """ Decorator similar to django login_required + """Decorator similar to django login_required Validates the request user against base_check instead """ + @wraps(func) def wrapper(request, *args, **kwargs): check = base_check(request, login_redirect=True) @@ -72,6 +74,7 @@ def wrapper(request, *args, **kwargs): return func(request, *args, **kwargs) else: return check + return wrapper @@ -84,21 +87,23 @@ def editor_is_not_author(func): """ def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) - decision = kwargs.get('decision', ED.REVIEW.value) + article_id = kwargs.get("article_id", None) + decision = kwargs.get("decision", ED.REVIEW.value) if not article_id: raise Http404 article = get_object_or_404(models.Article, pk=article_id) - if request.user in article.authors.all() and not article.editor_override(request.user): + if request.user in article.authors.all() and not article.editor_override( + request.user + ): return redirect( reverse( - 'review_warning', + "review_warning", kwargs={ - 'article_id': article.pk, - 'decision': decision, + "article_id": article.pk, + "decision": decision, }, ), ) @@ -109,7 +114,7 @@ def wrapper(request, *args, **kwargs): def senior_editor_user_required(func): - """ This decorator checks that a user is an editor, Note that this decorator does NOT check for conflict of interest + """This decorator checks that a user is an editor, Note that this decorator does NOT check for conflict of interest problems. Use the article_editor_user_required decorator (not yet written) to do a check against an article. :param func: the function to callback from the decorator @@ -118,7 +123,6 @@ def senior_editor_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) @@ -153,11 +157,13 @@ def production_manager_roles(func): :return: either the function call or permission denied """ - @base_check_required def wrapper(request, *args, **kwargs): - - if request.user.is_editor(request) or request.user.is_section_editor(request) or request.user.is_production(request): + if ( + request.user.is_editor(request) + or request.user.is_section_editor(request) + or request.user.is_production(request) + ): return func(request, *args, **kwargs) else: @@ -168,16 +174,18 @@ def wrapper(request, *args, **kwargs): def proofing_manager_roles(func): """ - Checks if the current user has one of the proofing manager roles. - :param func: the function to callback from the decorator - :return: either the function call or permission denied - """ + Checks if the current user has one of the proofing manager roles. + :param func: the function to callback from the decorator + :return: either the function call or permission denied + """ @base_check_required def wrapper(request, *args, **kwargs): - - if request.user.is_editor(request) or request.user.is_section_editor( - request) or request.user.is_proofing_manager(request): + if ( + request.user.is_editor(request) + or request.user.is_section_editor(request) + or request.user.is_proofing_manager(request) + ): return func(request, *args, **kwargs) else: @@ -191,22 +199,22 @@ def role_can_access(access_setting): This decorator determines if a user can access a given view based on the roles that are allowed to access it. """ + def decorator(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_staff: return func(request, *args, **kwargs) setting = setting_handler.get_setting( - setting_group_name='permission', + setting_group_name="permission", setting_name=access_setting, journal=request.journal, ) journal_roles = request.user.roles.get(request.journal.code) or set() setting_roles = set(setting.processed_value or []) - + # If no roles for the setting are configured we deny access # in the event that we want all roles to have access they # should be explicitly defined. @@ -214,7 +222,9 @@ def wrapper(request, *args, **kwargs): return func(request, *args, **kwargs) deny_access(request) + return wrapper + return decorator @@ -225,9 +235,8 @@ def user_can_edit_setting(func): """ def wrapper(request, *args, **kwargs): - - setting_group_name = kwargs.get('setting_group', None) - setting_name = kwargs.get('setting_name', None) + setting_group_name = kwargs.get("setting_group", None) + setting_name = kwargs.get("setting_name", None) if not setting_group_name or not setting_name: deny_access(request) @@ -254,15 +263,18 @@ def editor_or_journal_manager_required(func): This decorator checks that a user is either an editor a journal-manager. """ + @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_editor(request) or request.user.is_journal_manager(request.journal): + if request.user.is_editor(request) or request.user.is_journal_manager( + request.journal + ): return func(request, *args, **kwargs) deny_access(request) def editor_user_required(func): - """ This decorator checks that a user is an editor, or + """This decorator checks that a user is an editor, or that the user is a section editor assigned to the article in the url. Note that this decorator does NOT check for conflict of interest @@ -275,9 +287,13 @@ def editor_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) + article_id = kwargs.get("article_id", None) - if request.user.is_editor(request) or request.user.is_staff or request.user.is_journal_manager(request.journal): + if ( + request.user.is_editor(request) + or request.user.is_staff + or request.user.is_journal_manager(request.journal) + ): return func(request, *args, **kwargs) elif request.user.is_section_editor(request) and article_id: @@ -295,20 +311,21 @@ def wrapper(request, *args, **kwargs): def editor_user_required_and_can_see_pii(func): """Extends editor_user_required to check if SE can see PII""" + @editor_user_required def can_see_pii_decorator(request, *args, **kwargs): - article_id = kwargs.get('article_id') + article_id = kwargs.get("article_id") article = get_object_or_404(models.Article, pk=article_id) - if ( - request.user in article.section_editors() - and not can_see_pii(request, article) + if request.user in article.section_editors() and not can_see_pii( + request, article ): deny_access( request, "You cannot access this page yet, because it could reveal" - " personally identifiable information." + " personally identifiable information.", ) return func(request, *args, **kwargs) + return can_see_pii_decorator @@ -337,10 +354,13 @@ def section_editor_draft_decisions(func): :param func: the function to callback from the decorator :return: either the function call or raises an permissiondenied. """ + @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) - drafting = setting_handler.get_setting('general', 'draft_decisions', request.journal).value + article_id = kwargs.get("article_id", None) + drafting = setting_handler.get_setting( + "general", "draft_decisions", request.journal + ).value if request.user.is_section_editor(request) and article_id: article = get_object_or_404(models.Article, pk=article_id) @@ -353,7 +373,7 @@ def wrapper(request, *args, **kwargs): def reviewer_user_required(func): - """ This decorator checks that a user is a reviewer, Note that this decorator does NOT check for conflict of + """This decorator checks that a user is a reviewer, Note that this decorator does NOT check for conflict of interest problems. Use the article_editor_user_required decorator (not yet written) to do a check against an article. @@ -363,7 +383,6 @@ def reviewer_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_reviewer(request) or request.user.is_staff: return func(request, *args, **kwargs) else: @@ -373,7 +392,7 @@ def wrapper(request, *args, **kwargs): def author_user_required(func): - """ This decorator checks that a user is an author + """This decorator checks that a user is an author :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -390,7 +409,7 @@ def wrapper(request, *args, **kwargs): def article_author_required(func): - """ This decorator checks that a user is an author and is an author of the article + """This decorator checks that a user is an author and is an author of the article :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -398,8 +417,8 @@ def article_author_required(func): @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs['article_id'] - article = models.Article.get_article(request.journal, 'id', article_id) + article_id = kwargs["article_id"] + article = models.Article.get_article(request.journal, "id", article_id) if request.user.is_author(request) and article.user_is_author(request.user): return func(request, *args, **kwargs) @@ -410,7 +429,7 @@ def wrapper(request, *args, **kwargs): def proofreader_user_required(func): - """ This decorator checks that a user is a proofreader + """This decorator checks that a user is a proofreader :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -418,7 +437,6 @@ def proofreader_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_proofreader(request) or request.user.is_proofreader(request): return func(request, *args, **kwargs) else: @@ -428,7 +446,7 @@ def wrapper(request, *args, **kwargs): def copyeditor_user_required(func): - """ This decorator checks that a user is a copyeditor. + """This decorator checks that a user is a copyeditor. :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -436,7 +454,6 @@ def copyeditor_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_copyeditor(request) or request.user.is_copyeditor(request): return func(request, *args, **kwargs) else: @@ -446,7 +463,7 @@ def wrapper(request, *args, **kwargs): def copyeditor_for_copyedit_required(func): - """ This decorator checks that a user is a copyeditor and that they are the copyeditor for this article. + """This decorator checks that a user is a copyeditor and that they are the copyeditor for this article. :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -454,11 +471,16 @@ def copyeditor_for_copyedit_required(func): @base_check_required def wrapper(request, *args, **kwargs): + copyedit_id = kwargs["copyedit_id"] + copyedit = get_object_or_404( + copyediting_models.CopyeditAssignment, pk=copyedit_id + ) - copyedit_id = kwargs['copyedit_id'] - copyedit = get_object_or_404(copyediting_models.CopyeditAssignment, pk=copyedit_id) - - if request.user == copyedit.copyeditor and request.user.is_copyeditor(request) or request.user.is_staff: + if ( + request.user == copyedit.copyeditor + and request.user.is_copyeditor(request) + or request.user.is_staff + ): return func(request, *args, **kwargs) else: deny_access(request) @@ -467,7 +489,7 @@ def wrapper(request, *args, **kwargs): def typesetting_user_or_production_user_or_editor_required(func): - """ This decorator checks that a user is a production manager + """This decorator checks that a user is a production manager :param func: the function to callback from the decorator :return: either the function call or raises an Http403 @@ -475,9 +497,12 @@ def typesetting_user_or_production_user_or_editor_required(func): @base_check_required def wrapper(request, *args, **kwargs): - - if request.user.is_typesetter(request) or request.user.is_production(request) or \ - request.user.is_editor(request) or request.user.is_staff: + if ( + request.user.is_typesetter(request) + or request.user.is_production(request) + or request.user.is_editor(request) + or request.user.is_staff + ): return func(request, *args, **kwargs) else: deny_access(request) @@ -486,7 +511,7 @@ def wrapper(request, *args, **kwargs): def production_user_or_editor_required(func): - """ This decorator checks that a user is a production manager + """This decorator checks that a user is a production manager :param func: the function to callback from the decorator :return: either the function call or raises an Http403 @@ -494,10 +519,14 @@ def production_user_or_editor_required(func): @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) - typeset_id = kwargs.get('typeset_id', None) + article_id = kwargs.get("article_id", None) + typeset_id = kwargs.get("typeset_id", None) - if request.user.is_production(request) or request.user.is_editor(request) or request.user.is_staff: + if ( + request.user.is_production(request) + or request.user.is_editor(request) + or request.user.is_staff + ): return func(request, *args, **kwargs) elif article_id: @@ -512,7 +541,7 @@ def wrapper(request, *args, **kwargs): typeset_task = get_object_or_404( production_models.TypesetTask, pk=typeset_id, - assignment__article__journal=request.journal + assignment__article__journal=request.journal, ) if request.user in typeset_task.assignment.article.section_editors(): return func(request, *args, **kwargs) @@ -523,7 +552,7 @@ def wrapper(request, *args, **kwargs): def reviewer_user_for_assignment_required(func): - """ This decorator checks permissions for a user to accept or decline a review request. It also checks that the + """This decorator checks permissions for a user to accept or decline a review request. It also checks that the associated article is in a stage for which it is valid to perform this action. :param func: the function to callback from the decorator @@ -534,7 +563,7 @@ def wrapper(request, *args, **kwargs): from review import logic as reviewer_logic access_code = reviewer_logic.get_access_code(request) - assignment_id = kwargs['assignment_id'] + assignment_id = kwargs["assignment_id"] if not access_code: check = base_check(request, login_redirect=True) @@ -545,8 +574,9 @@ def wrapper(request, *args, **kwargs): if access_code is not None: try: - assignment = review_models.ReviewAssignment.objects.get(pk=assignment_id, - access_code=access_code) + assignment = review_models.ReviewAssignment.objects.get( + pk=assignment_id, access_code=access_code + ) if assignment: return func(request, *args, **kwargs) @@ -564,17 +594,20 @@ def wrapper(request, *args, **kwargs): try: if request.user.is_staff: - assignment = review_models.ReviewAssignment.objects.get(pk=assignment_id) + assignment = review_models.ReviewAssignment.objects.get( + pk=assignment_id + ) if assignment: return func(request, *args, **kwargs) else: deny_access(request) - assignment = review_models.ReviewAssignment.objects.get(pk=assignment_id, reviewer=request.user) + assignment = review_models.ReviewAssignment.objects.get( + pk=assignment_id, reviewer=request.user + ) if assignment: - if assignment.article.stage not in models.REVIEW_ACCESSIBLE_STAGES: deny_access(request) else: @@ -603,7 +636,7 @@ def a_view(request): @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id') + article_id = kwargs.get("article_id") if article_id: article = get_object_or_404( models.Article, @@ -624,7 +657,7 @@ def wrapper(request, *args, **kwargs): # Article-specific user enforcement def article_production_user_required(func): - """ This decorator checks permissions for a user to view production + """This decorator checks permissions for a user to view production information about a specific article :param func: the function to callback from the decorator @@ -633,25 +666,36 @@ def article_production_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): + article_id = kwargs["article_id"] - article_id = kwargs['article_id'] - - article = models.Article.get_article(request.journal, 'id', article_id) - assigned = get_object_or_404(production_models.ProductionAssignment, article=article) + article = models.Article.get_article(request.journal, "id", article_id) + assigned = get_object_or_404( + production_models.ProductionAssignment, article=article + ) # if the user is editor or section editor of the article - if request.user in article.section_editors() or request.user in article.editor_list(): + if ( + request.user in article.section_editors() + or request.user in article.editor_list() + ): return func(request, *args, **kwargs) # If article is in production and user is the production manager - if ((assigned.production_manager.pk == request.user.pk) and article.stage == models.STAGE_TYPESETTING) or request.user.is_staff: + if ( + (assigned.production_manager.pk == request.user.pk) + and article.stage == models.STAGE_TYPESETTING + ) or request.user.is_staff: return func(request, *args, **kwargs) # If article is in proofing and the user is the proofing manager if article.stage == models.STAGE_PROOFING: - proofing_assigned = get_object_or_404(proofing_models.ProofingAssignment, article=article) - if (request.user.is_proofing_manager and proofing_assigned.proofing_manager == request.user) or \ - request.user.is_staff: + proofing_assigned = get_object_or_404( + proofing_models.ProofingAssignment, article=article + ) + if ( + request.user.is_proofing_manager + and proofing_assigned.proofing_manager == request.user + ) or request.user.is_staff: return func(request, *args, **kwargs) else: @@ -661,20 +705,22 @@ def wrapper(request, *args, **kwargs): def article_stage_production_required(func): - """ This decorator checks that a specific article is in the typesetting stage + """This decorator checks that a specific article is in the typesetting stage :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - if 'typeset_id' in kwargs: - typesetting_assignment = production_models.TypesetTask.objects.get(pk=kwargs.get('typeset_id')) + if "typeset_id" in kwargs: + typesetting_assignment = production_models.TypesetTask.objects.get( + pk=kwargs.get("typeset_id") + ) article_id = typesetting_assignment.assignment.article.pk else: - article_id = kwargs['article_id'] + article_id = kwargs["article_id"] - article = models.Article.get_article(request.journal, 'id', article_id) + article = models.Article.get_article(request.journal, "id", article_id) if article and article.stage == models.STAGE_TYPESETTING: return func(request, *args, **kwargs) @@ -685,17 +731,19 @@ def wrapper(request, *args, **kwargs): def article_stage_accepted_or_later_required(func): - """ This decorator checks that a specific article has been accepted + """This decorator checks that a specific article has been accepted :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - identifier_type = kwargs['identifier_type'] - identifier = kwargs['identifier'] + identifier_type = kwargs["identifier_type"] + identifier = kwargs["identifier"] - article_object = models.Article.get_article(request.journal, identifier_type, identifier) + article_object = models.Article.get_article( + request.journal, identifier_type, identifier + ) if article_object is None or not article_object.is_accepted(): deny_access(request) @@ -706,7 +754,7 @@ def wrapper(request, *args, **kwargs): def article_stage_accepted_or_later_or_staff_required(func): - """ This decorator checks that a specific article has been accepted or the user is staff + """This decorator checks that a specific article has been accepted or the user is staff :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -714,19 +762,25 @@ def article_stage_accepted_or_later_or_staff_required(func): @wraps(func) def wrapper(request, *args, **kwargs): - identifier_type = kwargs['identifier_type'] - identifier = kwargs['identifier'] + identifier_type = kwargs["identifier_type"] + identifier = kwargs["identifier"] - article_object = models.Article.get_article(request.journal, identifier_type, identifier) + article_object = models.Article.get_article( + request.journal, identifier_type, identifier + ) - if not request.journal and request.site_type.code == 'press': - article_object = models.Article.get_press_article(request.press, identifier_type, identifier) + if not request.journal and request.site_type.code == "press": + article_object = models.Article.get_press_article( + request.press, identifier_type, identifier + ) if article_object is not None and article_object.is_accepted(): return func(request, *args, **kwargs) elif request.user.is_anonymous: deny_access(request) - elif article_object is not None and (request.user.is_editor(request) or request.user.is_staff): + elif article_object is not None and ( + request.user.is_editor(request) or request.user.is_staff + ): return func(request, *args, **kwargs) elif request.user in article_object.section_editors(): return func(request, *args, **kwargs) @@ -737,16 +791,16 @@ def wrapper(request, *args, **kwargs): def article_edit_user_required(func): - """ This decorator checks permissions for a user to edit a specific article + """This decorator checks permissions for a user to edit a specific article :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - article_id = kwargs['article_id'] + article_id = kwargs["article_id"] - article = models.Article.get_article(request.journal, 'id', article_id) + article = models.Article.get_article(request.journal, "id", article_id) if article.can_edit(request.user): return func(request, *args, **kwargs) @@ -758,15 +812,16 @@ def wrapper(request, *args, **kwargs): # File permissions + def file_user_required(func): - """ This decorator checks that a user has permission to view a file + """This decorator checks that a user has permission to view a file :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - file_id = kwargs['file_id'] + file_id = kwargs["file_id"] if file_id == "None": return func(request, *args, **kwargs) @@ -776,79 +831,99 @@ def wrapper(request, *args, **kwargs): if can_view_file(request, request.user, file_object): return func(request, *args, **kwargs) else: - messages.add_message(request, messages.ERROR, 'File is not accessible to this user.') + messages.add_message( + request, messages.ERROR, "File is not accessible to this user." + ) deny_access(request) return wrapper def file_history_user_required(func): - """ This decorator checks permissions for a user to view the history of a specific article + """This decorator checks permissions for a user to view the history of a specific article :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - file_object = get_object_or_404(core_models.File, pk=kwargs['file_id']) + file_object = get_object_or_404(core_models.File, pk=kwargs["file_id"]) try: - article = models.Article.get_article(request.journal, 'id', kwargs['article_id']) + article = models.Article.get_article( + request.journal, "id", kwargs["article_id"] + ) except KeyError: - article = models.Article.get_article(request.journal, kwargs['identifier_type'], kwargs['identifier']) + article = models.Article.get_article( + request.journal, kwargs["identifier_type"], kwargs["identifier"] + ) if can_view_file_history(request, request.user, file_object, article): return func(request, *args, **kwargs) - messages.add_message(request, messages.ERROR, 'File editing not accessible to this user.') + messages.add_message( + request, messages.ERROR, "File editing not accessible to this user." + ) deny_access(request) return wrapper def file_edit_user_required(func): - """ This decorator checks permissions for a user to edit a specific article + """This decorator checks permissions for a user to edit a specific article :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - file_object = get_object_or_404(core_models.File, pk=kwargs['file_id']) + file_object = get_object_or_404(core_models.File, pk=kwargs["file_id"]) try: - article = models.Article.get_article(request.journal, 'id', kwargs['article_id']) + article = models.Article.get_article( + request.journal, "id", kwargs["article_id"] + ) except KeyError: - article = models.Article.get_article(request.journal, kwargs['identifier_type'], kwargs['identifier']) + article = models.Article.get_article( + request.journal, kwargs["identifier_type"], kwargs["identifier"] + ) if can_edit_file(request, request.user, file_object, article): return func(request, *args, **kwargs) - messages.add_message(request, messages.ERROR, 'File editing not accessible to this user.') + messages.add_message( + request, messages.ERROR, "File editing not accessible to this user." + ) deny_access(request) return wrapper def data_figure_file(func): - """ This decorator checks that a file is a data or figure file in the specified article + """This decorator checks that a file is a data or figure file in the specified article :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ def wrapper(request, *args, **kwargs): - file_object = get_object_or_404(core_models.File, pk=kwargs['file_id']) + file_object = get_object_or_404(core_models.File, pk=kwargs["file_id"]) try: - article = models.Article.get_article(request.journal, 'id', kwargs['article_id']) + article = models.Article.get_article( + request.journal, "id", kwargs["article_id"] + ) except KeyError: - article = models.Article.get_article(request.journal, kwargs['identifier_type'], kwargs['identifier']) + article = models.Article.get_article( + request.journal, kwargs["identifier_type"], kwargs["identifier"] + ) if is_data_figure_file(file_object, article): return func(request, *args, **kwargs) - messages.add_message(request, messages.ERROR, 'File is not a data or figure file.') + messages.add_message( + request, messages.ERROR, "File is not a data or figure file." + ) deny_access(request) return wrapper @@ -856,8 +931,9 @@ def wrapper(request, *args, **kwargs): # General checks to avoid "raise Http404()" logic elsewhere + def has_request(func): - """ This decorator checks that the request object is not None + """This decorator checks that the request object is not None :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -873,7 +949,7 @@ def wrapper(request, *args, **kwargs): def has_journal(func): - """ This decorator checks that the request object is not None + """This decorator checks that the request object is not None :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -889,7 +965,7 @@ def wrapper(request, *args, **kwargs): def article_exists(func): - """ This decorator checks that a specific article has been accepted or the user is staff + """This decorator checks that a specific article has been accepted or the user is staff :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -897,11 +973,13 @@ def article_exists(func): def wrapper(request, *args, **kwargs): try: - article_object = models.Article.get_article(request.journal, 'id', kwargs['article_id']) + article_object = models.Article.get_article( + request.journal, "id", kwargs["article_id"] + ) except KeyError: - article_object = models.Article.get_article(request.journal, - kwargs['identifier_type'], - kwargs['identifier']) + article_object = models.Article.get_article( + request.journal, kwargs["identifier_type"], kwargs["identifier"] + ) if article_object is None: raise Http404() @@ -923,10 +1001,13 @@ def article_decision_not_made(func): @wraps(func) def wrapper(request, *args, **kwargs): try: - article_object = models.Article.objects.get(pk=kwargs['article_id'], journal=request.journal) + article_object = models.Article.objects.get( + pk=kwargs["article_id"], journal=request.journal + ) except KeyError: - article_object = review_models.ReviewAssignment.objects.get(pk=kwargs['review_id'], - article__journal=request.journal).article + article_object = review_models.ReviewAssignment.objects.get( + pk=kwargs["review_id"], article__journal=request.journal + ).article under_consideration = models.REVIEW_STAGES.copy() under_consideration.remove(models.STAGE_ACCEPTED) @@ -936,24 +1017,24 @@ def wrapper(request, *args, **kwargs): messages.add_message( request, messages.INFO, - 'This article is not in a review stage.', + "This article is not in a review stage.", ) return redirect( reverse( - 'review_in_review', - kwargs={'article_id': article_object.pk}, + "review_in_review", + kwargs={"article_id": article_object.pk}, ) ) else: messages.add_message( request, messages.WARNING, - 'This article is no longer under review.', + "This article is no longer under review.", ) return redirect( reverse( - 'review_in_review', - kwargs={'article_id': article_object.pk}, + "review_in_review", + kwargs={"article_id": article_object.pk}, ) ) @@ -961,7 +1042,7 @@ def wrapper(request, *args, **kwargs): def typesetter_user_required(func): - """ This decorator checks that a user is a typesetter. + """This decorator checks that a user is a typesetter. :param func: the function to callback from the decorator :return: either the function call or raises an Http404 @@ -969,7 +1050,6 @@ def typesetter_user_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_typesetter(request) or request.user.is_staff: return func(request, *args, **kwargs) else: @@ -980,17 +1060,17 @@ def wrapper(request, *args, **kwargs): def typesetter_or_editor_required(func): """ - This decorator pulls a typeset task and checks the current user is either an editor or a typesetter with - an active task. + This decorator pulls a typeset task and checks the current user is either an editor or a typesetter with + an active task. - :param func: the function to callback from the decorator - :return: either the function call or raises an PermissionDenied - """ + :param func: the function to callback from the decorator + :return: either the function call or raises an PermissionDenied + """ @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) - typeset_id = kwargs.get('typeset_id', None) + article_id = kwargs.get("article_id", None) + typeset_id = kwargs.get("typeset_id", None) if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) @@ -1001,8 +1081,11 @@ def wrapper(request, *args, **kwargs): pk=typeset_id, assignment__article__journal=request.journal, ) - if request.user == typeset_assignment.typesetter and not typeset_assignment.completed and \ - request.user.is_typesetter(request): + if ( + request.user == typeset_assignment.typesetter + and not typeset_assignment.completed + and request.user.is_typesetter(request) + ): return func(request, *args, **kwargs) if article_id: @@ -1028,9 +1111,13 @@ def proofing_manager_or_editor_required(func): @base_check_required def wrapper(request, *args, **kwargs): - article_id = kwargs.get('article_id', None) + article_id = kwargs.get("article_id", None) - if request.user.is_editor(request) or request.user.is_staff or request.user.is_proofing_manager(request): + if ( + request.user.is_editor(request) + or request.user.is_staff + or request.user.is_proofing_manager(request) + ): return func(request, *args, **kwargs) elif article_id: article = get_object_or_404( @@ -1055,11 +1142,10 @@ def proofing_manager_for_article_required(func): @base_check_required def wrapper(request, *args, **kwargs): + article = get_object_or_404(models.Article, pk=kwargs["article_id"]) - article = get_object_or_404(models.Article, pk=kwargs['article_id']) - - if not hasattr(article, 'proofingassignment'): - return redirect(reverse('proofing_list')) + if not hasattr(article, "proofingassignment"): + return redirect(reverse("proofing_list")) if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) @@ -1072,8 +1158,7 @@ def wrapper(request, *args, **kwargs): try: proofing_models.ProofingAssignment.objects.get( - article=article, - proofing_manager=request.user + article=article, proofing_manager=request.user ) return func(request, *args, **kwargs) except proofing_models.ProofingAssignment.DoesNotExist: @@ -1093,7 +1178,6 @@ def proofreader_or_typesetter_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) @@ -1114,11 +1198,9 @@ def proofreader_for_article_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if 'article_id' in kwargs: + if "article_id" in kwargs: article = get_object_or_404( - models.Article, - pk=kwargs['article_id'], - journal=request.journal + models.Article, pk=kwargs["article_id"], journal=request.journal ) else: article = None @@ -1126,20 +1208,17 @@ def wrapper(request, *args, **kwargs): if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) - #proofing manager - elif ( - article - and request.user == article.proofingassignment.proofing_manager - ): + # proofing manager + elif article and request.user == article.proofingassignment.proofing_manager: return func(request, *args, **kwargs) - #User is Assigned as proofreader, regardless of role + # User is Assigned as proofreader, regardless of role elif proofing_models.ProofingTask.objects.filter( - pk=kwargs['proofing_task_id'], - proofreader=request.user, - cancelled=False, - completed__isnull=True, - round__assignment__article__journal=request.journal + pk=kwargs["proofing_task_id"], + proofreader=request.user, + cancelled=False, + completed__isnull=True, + round__assignment__article__journal=request.journal, ).exists(): return func(request, *args, **kwargs) @@ -1158,20 +1237,21 @@ def typesetter_for_corrections_required(func): @base_check_required def wrapper(request, *args, **kwargs): - if request.user.is_editor(request) or request.user.is_staff: return func(request, *args, **kwargs) try: proofing_models.TypesetterProofingTask.objects.get( - pk=kwargs['typeset_task_id'], + pk=kwargs["typeset_task_id"], cancelled=False, completed__isnull=True, typesetter=request.user, - proofing_task__round__assignment__article__journal=request.journal) + proofing_task__round__assignment__article__journal=request.journal, + ) return func(request, *args, **kwargs) except proofing_models.TypesetterProofingTask.DoesNotExist: deny_access(request) + return wrapper @@ -1185,8 +1265,8 @@ def press_only(func): @base_check_required def wrapper(request, *args, **kwargs): if request.journal or request.repository: - messages.add_message(request, messages.INFO, 'This is a press only page.') - return redirect(reverse('core_manager_index')) + messages.add_message(request, messages.INFO, "This is a press only page.") + return redirect(reverse("core_manager_index")) return func(request, *args, **kwargs) @@ -1202,11 +1282,10 @@ def preprint_editor_or_author_required(func): @base_check_required def wrapper(request, *args, **kwargs): - preprint = get_object_or_404( preprint_models.Preprint, - pk=kwargs['preprint_id'], - repository=request.repository + pk=kwargs["preprint_id"], + repository=request.repository, ) if request.user == preprint.owner or request.user.is_staff: @@ -1233,15 +1312,21 @@ def is_article_preprint_editor(func): @base_check_required def wrapper(request, *args, **kwargs): if not base_check(request): - return redirect('{0}?next={1}'.format(reverse('core_login'), request.path_info)) + return redirect( + "{0}?next={1}".format(reverse("core_login"), request.path_info) + ) preprint = get_object_or_404( preprint_models.Preprint, - pk=kwargs['preprint_id'], - repository=request.repository + pk=kwargs["preprint_id"], + repository=request.repository, ) - if request.user in preprint.subject_editors() or request.user.is_staff or request.user.is_repository_manager(request.repository): + if ( + request.user in preprint.subject_editors() + or request.user.is_staff + or request.user.is_repository_manager(request.repository) + ): return func(request, *args, **kwargs) deny_access(request) @@ -1258,9 +1343,11 @@ def is_repository_manager(func): @base_check_required def preprint_manager_wrapper(request, *args, **kwargs): - if request.repository and request.user: - if request.user.is_staff or request.user in request.repository.managers.all(): + if ( + request.user.is_staff + or request.user in request.repository.managers.all() + ): return func(request, *args, **kwargs) deny_access(request) @@ -1269,23 +1356,22 @@ def preprint_manager_wrapper(request, *args, **kwargs): def deny_access(request, *args, **kwargs): - """ Wrapper for raising a PermissionDenied exception + """Wrapper for raising a PermissionDenied exception *args and **kwargs are passed to the PermissionDenied constructor :param request: A django HttpRequest """ try: ident = request.user.email - roles = list(request.user.accountrole_set.filter( - journal=request.journal)) + roles = list(request.user.accountrole_set.filter(journal=request.journal)) except AttributeError: ident = request.user roles = [] logger.info( - "[ACCESS_DENIED:{ident}:{request.path_info}]" - "[ROLES:{roles}]" - "".format(request=request, ident=ident, roles={r.role for r in roles}), + "[ACCESS_DENIED:{ident}:{request.path_info}]" "[ROLES:{roles}]" "".format( + request=request, ident=ident, roles={r.role for r in roles} + ), ) raise PermissionDenied(*args, **kwargs) @@ -1300,13 +1386,10 @@ def article_stage_review_required(func): def review_required_wrapper(request, article_id=None, *args, **kwargs): if not article_id: - logger.debug('404 thrown as no article_id in kwargs') + logger.debug("404 thrown as no article_id in kwargs") raise Http404 - article = get_object_or_404( - models.Article, - pk=article_id - ) + article = get_object_or_404(models.Article, pk=article_id) if not article.stage in models.REVIEW_STAGES: deny_access(request) @@ -1324,10 +1407,10 @@ def keyword_page_enabled(func): """ def keyword_page_enabled_wrapper(request, *args, **kwargs): - if not request.journal.get_setting('general', 'keyword_list_page'): + if not request.journal.get_setting("general", "keyword_list_page"): return redirect( reverse( - 'website_index', + "website_index", ) ) else: @@ -1346,9 +1429,11 @@ def submission_authorised(func): @base_check_required def submission_authorised_wrapper(request, *args, **kwargs): if ( - request.user.is_staff or - (request.journal and request.user in request.journal.editors()) or - (request.repository and request.user in request.repository.managers.all()) + request.user.is_staff + or (request.journal and request.user in request.journal.editors()) + or ( + request.repository and request.user in request.repository.managers.all() + ) ): return func(request, *args, **kwargs) @@ -1356,23 +1441,17 @@ def submission_authorised_wrapper(request, *args, **kwargs): if not preprint_models.RepositoryRole.objects.filter( repository=request.repository, user=request.user, - role__slug='author', + role__slug="author", ).exists(): - return redirect( - reverse( - 'request_submission_access' - ) - ) + return redirect(reverse("request_submission_access")) - if request.journal and request.journal.get_setting('general', 'limit_access_to_submission'): + if request.journal and request.journal.get_setting( + "general", "limit_access_to_submission" + ): if not request.user.is_author( request, ): - return redirect( - reverse( - 'request_submission_access' - ) - ) + return redirect(reverse("request_submission_access")) return func(request, *args, **kwargs) @@ -1383,9 +1462,10 @@ def article_is_not_submitted(func): """ Checks that an article is not already submitted. """ + @wraps(func) def _article_is_not_submitted(request, *args, **kwargs): - article_id = kwargs.get('article_id') + article_id = kwargs.get("article_id") try: article = models.Article.objects.get( pk=article_id, @@ -1394,7 +1474,7 @@ def _article_is_not_submitted(request, *args, **kwargs): ) return func(request, *args, **kwargs) except models.Article.DoesNotExist: - raise Http404('This article has already been submitted.') + raise Http404("This article has already been submitted.") return _article_is_not_submitted @@ -1412,14 +1492,15 @@ def my_view(request): If a setting is not found, this decorator will return PermissionDenied. """ + def decorator(func): @wraps(func) def inner(request, *args, **kwargs): # Check if we have a journal and if the setting returns True try: if request.journal and request.journal.get_setting( - group_name=setting_group_name, - setting_name=setting_name, + group_name=setting_group_name, + setting_name=setting_name, ): return func(request, *args, **kwargs) except core_models.Setting.DoesNotExist: @@ -1429,4 +1510,5 @@ def inner(request, *args, **kwargs): deny_access(request) return inner + return decorator diff --git a/src/security/logic.py b/src/security/logic.py index 277b7c4299..6a490bac56 100755 --- a/src/security/logic.py +++ b/src/security/logic.py @@ -22,27 +22,34 @@ def can_edit_file(request, user, file_object, article): # allow file editing when the user is a production manager and the piece is in production try: - production_assigned = production_models.ProductionAssignment.objects.get(article=article,) - - if (user.is_production(request) and production_assigned.production_manager.pk == user.pk) and \ - article.stage == submission_models.STAGE_TYPESETTING and file_object.is_galley: + production_assigned = production_models.ProductionAssignment.objects.get( + article=article, + ) + + if ( + ( + user.is_production(request) + and production_assigned.production_manager.pk == user.pk + ) + and article.stage == submission_models.STAGE_TYPESETTING + and file_object.is_galley + ): return True except production_models.ProductionAssignment.DoesNotExist: pass # allow file editing when the user is the proofing manager for this article try: - proofing_models.ProofingAssignment.objects.get(proofing_manager=user, - article=file_object.article) + proofing_models.ProofingAssignment.objects.get( + proofing_manager=user, article=file_object.article + ) return True except proofing_models.ProofingAssignment.DoesNotExist: pass # Allow access to typesetters in production prod_task = production_models.TypesetTask.objects.filter( - typesetter=request.user, - assignment__article=article, - completed__isnull=True + typesetter=request.user, assignment__article=article, completed__isnull=True ).exists() if prod_task: return True @@ -57,7 +64,6 @@ def can_edit_file(request, user, file_object, article): if correction_task: return True - # deny access to all others return False @@ -74,7 +80,7 @@ def can_view_file(request, user, file_object=None): if not file_object: return True # general conditions under which a file can be viewed - if file_object.privacy == 'public': + if file_object.privacy == "public": return True if user.is_anonymous: @@ -90,23 +96,27 @@ def can_view_file(request, user, file_object=None): # allow file editing when the user is the proofing manager for this article try: - proofing_models.ProofingAssignment.objects.get(proofing_manager=user, - article__pk=file_object.article_id, - completed__isnull=True) + proofing_models.ProofingAssignment.objects.get( + proofing_manager=user, + article__pk=file_object.article_id, + completed__isnull=True, + ) return True except proofing_models.ProofingAssignment.DoesNotExist: pass if file_object.article_id: if proofing_models.TypesetterProofingTask.objects.filter( - proofing_task__round__assignment__article__pk=file_object.article_id, - typesetter=request.user, - completed__isnull=True + proofing_task__round__assignment__article__pk=file_object.article_id, + typesetter=request.user, + completed__isnull=True, ).exists(): return True try: - production_assigned = production_models.ProductionAssignment.objects.get(article=file_object.article) + production_assigned = production_models.ProductionAssignment.objects.get( + article=file_object.article + ) typeset_assignments = production_assigned.active_typeset_tasks() typesetters = [task.typesetter for task in typeset_assignments] @@ -131,8 +141,8 @@ def is_data_figure_file(file_object, article_object): def can_see_pii(request, article): # Before doing anything, check the setting is enabled: se_pii_filter_enabled = setting_handler.get_setting( - setting_group_name='permission', - setting_name='se_pii_filter', + setting_group_name="permission", + setting_name="se_pii_filter", journal=article.journal, ).processed_value diff --git a/src/security/templatetags/securitytags.py b/src/security/templatetags/securitytags.py index 52599950c2..eba15d8129 100755 --- a/src/security/templatetags/securitytags.py +++ b/src/security/templatetags/securitytags.py @@ -11,13 +11,13 @@ @register.simple_tag(takes_context=True) def is_author(context): - request = context['request'] + request = context["request"] return request.user.is_author(request) @register.simple_tag(takes_context=True) def is_editor(context): - request = context['request'] + request = context["request"] if request.user.is_anonymous: return False return request.user.is_editor(request) @@ -25,7 +25,7 @@ def is_editor(context): @register.simple_tag(takes_context=True) def is_section_editor(context): - request = context['request'] + request = context["request"] if request.user.is_anonymous: return False return request.user.is_section_editor(request) @@ -33,7 +33,7 @@ def is_section_editor(context): @register.simple_tag(takes_context=True) def is_any_editor(context): - request = context['request'] + request = context["request"] if request.user.is_anonymous: return False return request.user.has_an_editor_role(request) @@ -41,54 +41,59 @@ def is_any_editor(context): @register.simple_tag(takes_context=True) def is_production(context): - request = context['request'] + request = context["request"] return request.user.is_production(request) @register.simple_tag(takes_context=True) def is_reviewer(context): - request = context['request'] + request = context["request"] return request.user.is_reviewer(request) @register.simple_tag(takes_context=True) def is_proofreader(context): - request = context['request'] + request = context["request"] return request.user.is_proofreader(request) + # File-based checks @register.simple_tag(takes_context=True) def can_edit_file(context, file_object, article_object): - return logic.can_edit_file(context['request'], context['request'].user, file_object, article_object) + return logic.can_edit_file( + context["request"], context["request"].user, file_object, article_object + ) @register.simple_tag(takes_context=True) def can_view_file_history(context, file_object, article_object): - return logic.can_view_file_history(context['request'], context['request'].user, file_object, article_object) + return logic.can_view_file_history( + context["request"], context["request"].user, file_object, article_object + ) @register.simple_tag(takes_context=True) def can_view_file(context, file_object): - return logic.can_view_file(context['request'], context['request'].user, file_object) + return logic.can_view_file(context["request"], context["request"].user, file_object) @register.simple_tag(takes_context=True) def is_author(context): - request = context['request'] + request = context["request"] return request.user.is_author(request) @register.simple_tag(takes_context=True) def is_repository_manager(context): - request = context['request'] + request = context["request"] return request.user.is_repository_manager(request.repository) @register.simple_tag(takes_context=True) def is_preprint_editor(context): - request = context['request'] + request = context["request"] return request.user.is_preprint_editor(request) @@ -99,17 +104,14 @@ def se_can_see_pii(value, article): if logic.can_see_pii(request, article): return value else: - return _('[Anonymised data]') + return _("[Anonymised data]") @register.simple_tag(takes_context=True) def can_see_pii_tag(context, article): - request = context.get('request') + request = context.get("request") if logic.can_see_pii(request, article): return True else: return False - - - diff --git a/src/security/test_security.py b/src/security/test_security.py index ac4c9536e9..5f5f54f1b5 100644 --- a/src/security/test_security.py +++ b/src/security/test_security.py @@ -43,8 +43,10 @@ def test_account_is_editor_role_check_blocks_non_editors(self): request = self.prepare_request_with_user(self.regular_user, self.journal_one) # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.is_editor(request), - "Account.is_editor wrongly allows non-editors to access editor content") + self.assertFalse( + self.regular_user.is_editor(request), + "Account.is_editor wrongly allows non-editors to access editor content", + ) def test_account_role_check_blocks_non_editors(self): """ @@ -53,8 +55,10 @@ def test_account_role_check_blocks_non_editors(self): """ # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.check_role(self.journal_one, "editor"), - "Account.check_role wrongly allows non-editors to access editor content") + self.assertFalse( + self.regular_user.check_role(self.journal_one, "editor"), + "Account.check_role wrongly allows non-editors to access editor content", + ) def test_account_is_editor_role_check_allows_editors(self): """ @@ -64,8 +68,10 @@ def test_account_is_editor_role_check_allows_editors(self): request = self.prepare_request_with_user(self.editor, self.journal_one) # test the internal user role handling for non-editors - self.assertTrue(self.editor.is_editor(request), - "Account.is_editor wrongly blocks editors from accessing editor content") + self.assertTrue( + self.editor.is_editor(request), + "Account.is_editor wrongly blocks editors from accessing editor content", + ) def test_account_role_check_allows_editors(self): """ @@ -74,8 +80,10 @@ def test_account_role_check_allows_editors(self): """ # test the internal user role handling for non-editors - self.assertTrue(self.editor.check_role(self.journal_one, "editor"), - "Account.check_role wrongly blocks editors from accessing editor content") + self.assertTrue( + self.editor.check_role(self.journal_one, "editor"), + "Account.check_role wrongly blocks editors from accessing editor content", + ) def test_reviewer_user_required_decorator_handles_null_user(self): """ @@ -90,8 +98,10 @@ def test_reviewer_user_required_decorator_handles_null_user(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "reviewer_user_required decorator incorrectly handles request.user=None", + ) def test_reviewer_user_required_decorator_blocks_anonymous_users(self): """ @@ -107,8 +117,10 @@ def test_reviewer_user_required_decorator_blocks_anonymous_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows anonymous users to access editor content") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows anonymous users to access editor content", + ) def test_reviewer_user_required_decorator_blocks_non_reviewers(self): """ @@ -126,8 +138,10 @@ def test_reviewer_user_required_decorator_blocks_non_reviewers(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows non-editors to access editor content") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows non-editors to access editor content", + ) def test_reviewer_user_required_decorator_allows_editors(self): """ @@ -144,8 +158,11 @@ def test_reviewer_user_required_decorator_allows_editors(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "reviewer_user_required decorator wrongly prohibits editors from accessing " - "content") + self.assertTrue( + func.called, + "reviewer_user_required decorator wrongly prohibits editors from accessing " + "content", + ) def test_reviewer_user_required_decorator_allows_staff(self): """ @@ -161,7 +178,10 @@ def test_reviewer_user_required_decorator_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "reviewer_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "reviewer_user_required decorator wrongly prohibits staff from accessing content", + ) def test_reviewer_user_required_decorator_isolates_journals(self): """ @@ -181,9 +201,11 @@ def test_reviewer_user_required_decorator_isolates_journals(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows editors to access editor content on other " - "journals") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows editors to access editor content on other " + "journals", + ) def test_reviewer_user_required_decorator_blocks_authors(self): """ @@ -200,8 +222,10 @@ def test_reviewer_user_required_decorator_blocks_authors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows authors to access editor content") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows authors to access editor content", + ) def test_reviewer_user_required_decorator_blocks_proofreaders(self): """ @@ -218,8 +242,10 @@ def test_reviewer_user_required_decorator_blocks_proofreaders(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows proofreaders to access editor content") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows proofreaders to access editor content", + ) def test_reviewer_user_required_decorator_blocks_inactive_users(self): """ @@ -234,8 +260,10 @@ def test_reviewer_user_required_decorator_blocks_inactive_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_required decorator wrongly allows inactive users to access author content") + self.assertFalse( + func.called, + "reviewer_user_required decorator wrongly allows inactive users to access author content", + ) def test_editor_user_required_decorator_handles_null_user(self): """ @@ -250,8 +278,10 @@ def test_editor_user_required_decorator_handles_null_user(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "editor_user_required decorator incorrectly handles request.user=None", + ) def test_editor_user_required_decorator_blocks_anonymous_users(self): """ @@ -267,8 +297,10 @@ def test_editor_user_required_decorator_blocks_anonymous_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows anonymous users to access editor content") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows anonymous users to access editor content", + ) def test_editor_user_required_decorator_blocks_non_editors(self): """ @@ -286,8 +318,10 @@ def test_editor_user_required_decorator_blocks_non_editors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows non-editors to access editor content") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows non-editors to access editor content", + ) def test_editor_user_required_decorator_allows_editors(self): """ @@ -304,7 +338,10 @@ def test_editor_user_required_decorator_allows_editors(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "editor_user_required decorator wrongly prohibits editors from accessing content") + self.assertTrue( + func.called, + "editor_user_required decorator wrongly prohibits editors from accessing content", + ) def test_editor_user_required_decorator_allows_staff(self): """ @@ -320,7 +357,10 @@ def test_editor_user_required_decorator_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "editor_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "editor_user_required decorator wrongly prohibits staff from accessing content", + ) def test_editor_user_required_decorator_isolates_journals(self): """ @@ -340,9 +380,11 @@ def test_editor_user_required_decorator_isolates_journals(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows editors to access editor content on other " - "journals") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows editors to access editor content on other " + "journals", + ) def test_editor_user_required_decorator_blocks_authors(self): """ @@ -359,8 +401,10 @@ def test_editor_user_required_decorator_blocks_authors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows authors to access editor content") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows authors to access editor content", + ) def test_editor_user_required_decorator_blocks_proofreaders(self): """ @@ -377,8 +421,10 @@ def test_editor_user_required_decorator_blocks_proofreaders(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows proofreaders to access editor content") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows proofreaders to access editor content", + ) def test_editor_user_required_decorator_blocks_inactive_users(self): """ @@ -393,8 +439,10 @@ def test_editor_user_required_decorator_blocks_inactive_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "editor_user_required decorator wrongly allows inactive users to access author content") + self.assertFalse( + func.called, + "editor_user_required decorator wrongly allows inactive users to access author content", + ) # Tests for author role checks def test_account_is_author_role_check_blocks_non_authors(self): @@ -405,8 +453,10 @@ def test_account_is_author_role_check_blocks_non_authors(self): request = self.prepare_request_with_user(self.regular_user, self.journal_one) # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.is_editor(request), - "Account.is_author wrongly allows non-authors to access author content") + self.assertFalse( + self.regular_user.is_editor(request), + "Account.is_author wrongly allows non-authors to access author content", + ) def test_account_role_check_blocks_non_authors(self): """ @@ -415,8 +465,10 @@ def test_account_role_check_blocks_non_authors(self): """ # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.check_role(self.journal_one, "author"), - "Account.check_role wrongly allows non-authors to access author content") + self.assertFalse( + self.regular_user.check_role(self.journal_one, "author"), + "Account.check_role wrongly allows non-authors to access author content", + ) def test_account_is_author_role_check_allows_authors(self): """ @@ -426,8 +478,10 @@ def test_account_is_author_role_check_allows_authors(self): request = self.prepare_request_with_user(self.editor, self.journal_one) # test the internal user role handling for non-editors - self.assertTrue(self.author.is_author(request), - "Account.is_editor wrongly blocks authors from accessing author content") + self.assertTrue( + self.author.is_author(request), + "Account.is_editor wrongly blocks authors from accessing author content", + ) def test_account_role_check_allows_authors(self): """ @@ -436,8 +490,10 @@ def test_account_role_check_allows_authors(self): """ # test the internal user role handling for non-editors - self.assertTrue(self.author.check_role(self.journal_one, "author"), - "Account.check_role wrongly blocks authors from accessing author content") + self.assertTrue( + self.author.check_role(self.journal_one, "author"), + "Account.check_role wrongly blocks authors from accessing author content", + ) def test_author_user_required_decorator_blocks_anonymous_users(self): """ @@ -453,8 +509,10 @@ def test_author_user_required_decorator_blocks_anonymous_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows anonymous users to access author content") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows anonymous users to access author content", + ) def test_author_user_required_decorator_handles_null_user(self): """ @@ -469,8 +527,10 @@ def test_author_user_required_decorator_handles_null_user(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "author_user_required decorator incorrectly handles request.user=None", + ) def test_author_user_required_decorator_blocks_non_authors(self): """ @@ -488,8 +548,10 @@ def test_author_user_required_decorator_blocks_non_authors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows non-authors to access author content") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows non-authors to access author content", + ) def test_author_user_required_decorator_allows_authors(self): """ @@ -506,7 +568,10 @@ def test_author_user_required_decorator_allows_authors(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "author_user_required decorator wrongly prohibits authors from accessing content") + self.assertTrue( + func.called, + "author_user_required decorator wrongly prohibits authors from accessing content", + ) def test_author_user_required_decorator_allows_staff(self): """ @@ -522,7 +587,10 @@ def test_author_user_required_decorator_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, "author_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "author_user_required decorator wrongly prohibits staff from accessing content", + ) def test_author_user_required_decorator_isolates_journals(self): """ @@ -542,9 +610,11 @@ def test_author_user_required_decorator_isolates_journals(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows authors to access editor content on other " - "journals") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows authors to access editor content on other " + "journals", + ) def test_author_user_required_decorator_blocks_editors(self): """ @@ -561,8 +631,10 @@ def test_author_user_required_decorator_blocks_editors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows editors to access author content") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows editors to access author content", + ) def test_author_user_required_decorator_blocks_proofreaders(self): """ @@ -579,8 +651,10 @@ def test_author_user_required_decorator_blocks_proofreaders(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows proofreaders to access author content") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows proofreaders to access author content", + ) def test_author_user_required_decorator_blocks_inactive_users(self): """ @@ -595,8 +669,10 @@ def test_author_user_required_decorator_blocks_inactive_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "author_user_required decorator wrongly allows inactive users to access author content") + self.assertFalse( + func.called, + "author_user_required decorator wrongly allows inactive users to access author content", + ) # Tests for proofreader role checks def test_account_is_proofreader_role_check_blocks_non_proofreaders(self): @@ -607,8 +683,10 @@ def test_account_is_proofreader_role_check_blocks_non_proofreaders(self): request = self.prepare_request_with_user(self.regular_user, self.journal_one) # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.is_editor(request), - "Account.is_proofreader wrongly allows non-proofreaders to access proofreader content") + self.assertFalse( + self.regular_user.is_editor(request), + "Account.is_proofreader wrongly allows non-proofreaders to access proofreader content", + ) def test_account_role_check_blocks_non_proofreaders(self): """ @@ -617,8 +695,10 @@ def test_account_role_check_blocks_non_proofreaders(self): """ # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.check_role(self.journal_one, "proofreader"), - "Account.check_role wrongly allows non-proofreaders to access proofreader content") + self.assertFalse( + self.regular_user.check_role(self.journal_one, "proofreader"), + "Account.check_role wrongly allows non-proofreaders to access proofreader content", + ) def test_account_is_proofreader_role_check_allows_proofreaders(self): """ @@ -628,8 +708,10 @@ def test_account_is_proofreader_role_check_allows_proofreaders(self): request = self.prepare_request_with_user(self.editor, self.journal_one) # test the internal user role handling for non-editors - self.assertTrue(self.proofreader.is_proofreader(request), - "Account.is_editor wrongly blocks proofreaders from accessing proofreader content") + self.assertTrue( + self.proofreader.is_proofreader(request), + "Account.is_editor wrongly blocks proofreaders from accessing proofreader content", + ) def test_account_role_check_allows_proofreaders(self): """ @@ -638,8 +720,10 @@ def test_account_role_check_allows_proofreaders(self): """ # test the internal user role handling for non-editors - self.assertTrue(self.proofreader.check_role(self.journal_one, "proofreader"), - "Account.check_role wrongly blocks proofreaders from accessing proofreader content") + self.assertTrue( + self.proofreader.check_role(self.journal_one, "proofreader"), + "Account.check_role wrongly blocks proofreaders from accessing proofreader content", + ) def test_proofreader_user_required_decorator_blocks_anonymous_users(self): """ @@ -655,9 +739,11 @@ def test_proofreader_user_required_decorator_blocks_anonymous_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows anonymous users to access proofreader " - "content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows anonymous users to access proofreader " + "content", + ) def test_proofreader_user_required_decorator_handles_null_user(self): """ @@ -672,8 +758,10 @@ def test_proofreader_user_required_decorator_handles_null_user(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "proofreader_user_required decorator incorrectly handles request.user=None", + ) def test_proofreader_user_required_decorator_blocks_non_proofreaders(self): """ @@ -691,9 +779,11 @@ def test_proofreader_user_required_decorator_blocks_non_proofreaders(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows non-proofreaders to access proofreader " - "content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows non-proofreaders to access proofreader " + "content", + ) def test_proofreader_user_required_decorator_allows_proofreaders(self): """ @@ -710,8 +800,10 @@ def test_proofreader_user_required_decorator_allows_proofreaders(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "proofreader_user_required decorator wrongly prohibits proofreaders from accessing content") + self.assertTrue( + func.called, + "proofreader_user_required decorator wrongly prohibits proofreaders from accessing content", + ) def test_proofreader_user_required_decorator_allows_staff(self): """ @@ -727,8 +819,10 @@ def test_proofreader_user_required_decorator_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "proofreader_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "proofreader_user_required decorator wrongly prohibits staff from accessing content", + ) def test_proofreader_user_required_decorator_isolates_journals(self): """ @@ -747,9 +841,11 @@ def test_proofreader_user_required_decorator_isolates_journals(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows proofreaders to access editor content on " - "other journals") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows proofreaders to access editor content on " + "other journals", + ) def test_proofreader_user_required_decorator_blocks_editors(self): """ @@ -766,8 +862,10 @@ def test_proofreader_user_required_decorator_blocks_editors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows editors to access proofreader content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows editors to access proofreader content", + ) def test_proofreader_user_required_decorator_blocks_authors(self): """ @@ -783,8 +881,10 @@ def test_proofreader_user_required_decorator_blocks_authors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows authors to access proofreader content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows authors to access proofreader content", + ) def test_proofreader_user_required_decorator_blocks_productions(self): """ @@ -800,8 +900,10 @@ def test_proofreader_user_required_decorator_blocks_productions(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows authors to access proofreader content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows authors to access proofreader content", + ) def test_proofreader_user_required_decorator_blocks_inactive_users(self): """ @@ -816,9 +918,11 @@ def test_proofreader_user_required_decorator_blocks_inactive_users(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "proofreader_user_required decorator wrongly allows inactive users to access proofreader " - "content") + self.assertFalse( + func.called, + "proofreader_user_required decorator wrongly allows inactive users to access proofreader " + "content", + ) # Tests for production role checks def test_account_is_production_role_check_blocks_non_productions(self): @@ -829,8 +933,10 @@ def test_account_is_production_role_check_blocks_non_productions(self): request = self.prepare_request_with_user(self.regular_user, self.journal_one) # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.is_editor(request), - "Account.is_production wrongly allows non-productions to access production content") + self.assertFalse( + self.regular_user.is_editor(request), + "Account.is_production wrongly allows non-productions to access production content", + ) def test_account_role_check_blocks_non_productions(self): """ @@ -839,8 +945,10 @@ def test_account_role_check_blocks_non_productions(self): """ # test the internal user role handling for non-editors - self.assertFalse(self.regular_user.check_role(self.journal_one, "production"), - "Account.check_role wrongly allows non-productions to access production content") + self.assertFalse( + self.regular_user.check_role(self.journal_one, "production"), + "Account.check_role wrongly allows non-productions to access production content", + ) def test_account_is_production_role_check_allows_productions(self): """ @@ -850,8 +958,10 @@ def test_account_is_production_role_check_allows_productions(self): request = self.prepare_request_with_user(self.editor, self.journal_one) # test the internal user role handling for non-editors - self.assertTrue(self.production.is_production(request), - "Account.is_editor wrongly blocks productions from accessing production content") + self.assertTrue( + self.production.is_production(request), + "Account.is_editor wrongly blocks productions from accessing production content", + ) def test_account_role_check_allows_productions(self): """ @@ -860,8 +970,10 @@ def test_account_role_check_allows_productions(self): """ # test the internal user role handling for non-editors - self.assertTrue(self.production.check_role(self.journal_one, "production"), - "Account.check_role wrongly blocks productions from accessing production content") + self.assertTrue( + self.production.check_role(self.journal_one, "production"), + "Account.check_role wrongly blocks productions from accessing production content", + ) def test_production_user_or_editor_required_decorator_blocks_anonymous_users(self): """ @@ -877,9 +989,11 @@ def test_production_user_or_editor_required_decorator_blocks_anonymous_users(sel self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator wrongly allows anonymous users to access " - "production content") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator wrongly allows anonymous users to access " + "production content", + ) def test_production_user_or_editor_required_decorator_blocks_non_productions(self): """ @@ -897,9 +1011,11 @@ def test_production_user_or_editor_required_decorator_blocks_non_productions(sel decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator wrongly allows non-productions to access " - "production content") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator wrongly allows non-productions to access " + "production content", + ) def test_production_user_or_editor_required_decorator_handles_null_user(self): """ @@ -914,8 +1030,10 @@ def test_production_user_or_editor_required_decorator_handles_null_user(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator incorrectly handles request.user=None", + ) def test_production_user_or_editor_required_decorator_allows_productions(self): """ @@ -932,9 +1050,11 @@ def test_production_user_or_editor_required_decorator_allows_productions(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "production_user_or_editor_required decorator wrongly prohibits productions from accessing " - "content") + self.assertTrue( + func.called, + "production_user_or_editor_required decorator wrongly prohibits productions from accessing " + "content", + ) def test_production_user_or_editor_required_decorator_allows_staff(self): """ @@ -950,8 +1070,10 @@ def test_production_user_or_editor_required_decorator_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "production_user_or_editor_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "production_user_or_editor_required decorator wrongly prohibits staff from accessing content", + ) def test_production_user_or_editor_required_decorator_isolates_journals(self): """ @@ -970,9 +1092,11 @@ def test_production_user_or_editor_required_decorator_isolates_journals(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator wrongly allows productions to access editor " - "content on other journals") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator wrongly allows productions to access editor " + "content on other journals", + ) def test_production_user_or_editor_required_decorator_allows_editors(self): """ @@ -987,9 +1111,11 @@ def test_production_user_or_editor_required_decorator_allows_editors(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "production_user_or_editor_required decorator wrongly allows editors to access production " - "content") + self.assertTrue( + func.called, + "production_user_or_editor_required decorator wrongly allows editors to access production " + "content", + ) def test_production_user_or_editor_required_decorator_blocks_authors(self): """ @@ -1005,9 +1131,11 @@ def test_production_user_or_editor_required_decorator_blocks_authors(self): decorated_func(request) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator wrongly allows authors to access production " - "content") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator wrongly allows authors to access production " + "content", + ) def test_production_user_or_editor_required_decorator_blocks_inactive_users(self): """ @@ -1022,9 +1150,11 @@ def test_production_user_or_editor_required_decorator_blocks_inactive_users(self self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "production_user_or_editor_required decorator wrongly allows inactive users to access " - "production content") + self.assertFalse( + func.called, + "production_user_or_editor_required decorator wrongly allows inactive users to access " + "production content", + ) # Tests for article_production_user_required def test_article_production_user_required_allows_production(self): @@ -1036,14 +1166,16 @@ def test_article_production_user_required_allows_production(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.production, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "article_production_user_required decorator wrongly prohibits the production manager from " - "accessing an article to which he or she is assigned") + self.assertTrue( + func.called, + "article_production_user_required decorator wrongly prohibits the production manager from " + "accessing an article to which he or she is assigned", + ) def test_article_production_user_required_decorator_handles_null_user(self): """ @@ -1054,13 +1186,15 @@ def test_article_production_user_required_decorator_handles_null_user(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(None, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "article_production_user_required decorator incorrectly handles request.user=None", + ) def test_article_production_user_required_allows_staff(self): """ @@ -1071,14 +1205,16 @@ def test_article_production_user_required_allows_staff(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "article_production_user_required decorator wrongly prohibits staff from " - "accessing an article in production") + self.assertTrue( + func.called, + "article_production_user_required decorator wrongly prohibits staff from " + "accessing an article in production", + ) def test_article_production_user_required_allows_staff_regardless_of_stage(self): """ @@ -1089,14 +1225,16 @@ def test_article_production_user_required_allows_staff_regardless_of_stage(self) decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'article_id': self.article_published.pk} + kwargs = {"article_id": self.article_published.pk} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "article_production_user_required decorator wrongly prohibits staff from " - "accessing an article that has been published's production stage") + self.assertTrue( + func.called, + "article_production_user_required decorator wrongly prohibits staff from " + "accessing an article that has been published's production stage", + ) def test_article_production_user_required_blocks_editor(self): """ @@ -1107,15 +1245,17 @@ def test_article_production_user_required_blocks_editor(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.editor, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) def test_article_production_user_required_blocks_author(self): """ @@ -1126,15 +1266,17 @@ def test_article_production_user_required_blocks_author(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.author, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) def test_article_production_user_required_blocks_regular_users(self): """ @@ -1145,17 +1287,21 @@ def test_article_production_user_required_blocks_regular_users(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) - def test_article_production_user_required_blocks_production_when_stage_not_set_to_production(self): + def test_article_production_user_required_blocks_production_when_stage_not_set_to_production( + self, + ): """ Tests that article_production_user_required blocks the correct production user when the stage is not set to Typesetting. @@ -1165,15 +1311,17 @@ def test_article_production_user_required_blocks_production_when_stage_not_set_t decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.production, self.journal_one) - kwargs = {'article_id': self.article_published.pk} + kwargs = {"article_id": self.article_published.pk} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows a production manager to " - "access an article that is not in production") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows a production manager to " + "access an article that is not in production", + ) def test_article_production_user_required_decorator_blocks_inactive_users(self): """ @@ -1184,14 +1332,16 @@ def test_article_production_user_required_decorator_blocks_inactive_users(self): decorated_func = decorators.article_production_user_required(func) request = self.prepare_request_with_user(self.inactive_user, self.journal_one) - kwargs = {'article_id': self.article_published.pk} + kwargs = {"article_id": self.article_published.pk} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows inactive users to access " - "production content") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows inactive users to access " + "production content", + ) def test_article_production_user_required_decorator_blocks_anonymous_users(self): """ @@ -1203,14 +1353,16 @@ def test_article_production_user_required_decorator_blocks_anonymous_users(self) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_published.pk} + kwargs = {"article_id": self.article_published.pk} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "article_production_user_required decorator wrongly allows anonymous users to access " - "production content") + self.assertFalse( + func.called, + "article_production_user_required decorator wrongly allows anonymous users to access " + "production content", + ) def test_article_production_user_required_allows_assigned_proofing_manager(self): """ @@ -1219,21 +1371,24 @@ def test_article_production_user_required_allows_assigned_proofing_manager(self) """ proofing_models.ProofingAssignment.objects.create( - article=self.article_in_proofing, - proofing_manager=self.proofing_manager + article=self.article_in_proofing, proofing_manager=self.proofing_manager ) func = Mock() decorated_func = decorators.article_production_user_required(func) - request = self.prepare_request_with_user(self.proofing_manager, self.journal_one) - kwargs = {'article_id': self.article_in_proofing.pk} + request = self.prepare_request_with_user( + self.proofing_manager, self.journal_one + ) + kwargs = {"article_id": self.article_in_proofing.pk} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "article_production_user_required decorator wrongly prohibits proofing manager") + self.assertTrue( + func.called, + "article_production_user_required decorator wrongly prohibits proofing manager", + ) # Tests for reviewer_user_for_assignment_required def test_reviewer_user_for_assignment_required_allows_reviewer(self): @@ -1245,14 +1400,16 @@ def test_reviewer_user_for_assignment_required_allows_reviewer(self): decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.second_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "reviewer_user_for_assignment_required decorator wrongly prohibits the reviewer from " - "accessing an article to which he or she is assigned") + self.assertTrue( + func.called, + "reviewer_user_for_assignment_required decorator wrongly prohibits the reviewer from " + "accessing an article to which he or she is assigned", + ) def test_reviewer_user_for_assignment_required_decorator_handles_null_user(self): """ @@ -1263,13 +1420,15 @@ def test_reviewer_user_for_assignment_required_decorator_handles_null_user(self) decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(None, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator incorrectly handles request.user=None") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator incorrectly handles request.user=None", + ) def test_reviewer_user_for_assignment_required_allows_staff(self): """ @@ -1280,16 +1439,20 @@ def test_reviewer_user_for_assignment_required_allows_staff(self): decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "reviewer_user_for_assignment_required decorator wrongly prohibits staff from " - "accessing an article in production") + self.assertTrue( + func.called, + "reviewer_user_for_assignment_required decorator wrongly prohibits staff from " + "accessing an article in production", + ) - def test_reviewer_user_for_assignment_required_allows_staff_regardless_of_stage(self): + def test_reviewer_user_for_assignment_required_allows_staff_regardless_of_stage( + self, + ): """ Tests that reviewer_user_for_assignment_required allows staff to article in review, regardless of stage. :return: None or raises an assertion @@ -1298,14 +1461,16 @@ def test_reviewer_user_for_assignment_required_allows_staff_regardless_of_stage( decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "reviewer_user_for_assignment_required decorator wrongly prohibits staff from " - "accessing an article that has been published's production stage") + self.assertTrue( + func.called, + "reviewer_user_for_assignment_required decorator wrongly prohibits staff from " + "accessing an article that has been published's production stage", + ) def test_reviewer_user_for_assignment_required_blocks_editor(self): """ @@ -1316,15 +1481,17 @@ def test_reviewer_user_for_assignment_required_blocks_editor(self): decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.editor, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) def test_reviewer_user_for_assignment_required_blocks_author(self): """ @@ -1335,15 +1502,17 @@ def test_reviewer_user_for_assignment_required_blocks_author(self): decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.author, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) def test_reviewer_user_for_assignment_required_blocks_regular_users(self): """ @@ -1354,17 +1523,21 @@ def test_reviewer_user_for_assignment_required_blocks_regular_users(self): decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.second_reviewer, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows an editor to " - "access an article in production to which he or she is assigned") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows an editor to " + "access an article in production to which he or she is assigned", + ) - def test_reviewer_user_for_assignment_required_blocks_review_when_stage_not_set_to_review(self): + def test_reviewer_user_for_assignment_required_blocks_review_when_stage_not_set_to_review( + self, + ): """ Tests that reviewer_user_for_assignment_required blocks the correct production user when the stage is not set to Typesetting. @@ -1374,17 +1547,21 @@ def test_reviewer_user_for_assignment_required_blocks_review_when_stage_not_set_ decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment_not_in_scope.id} + kwargs = {"assignment_id": self.review_assignment_not_in_scope.id} with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows a reviewer to " - "access an article that is not in a review stage") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows a reviewer to " + "access an article that is not in a review stage", + ) - def test_reviewer_user_for_assignment_required_decorator_blocks_inactive_users(self): + def test_reviewer_user_for_assignment_required_decorator_blocks_inactive_users( + self, + ): """ Tests that the reviewer_user_for_assignment_required decorator does not allow an inactive to view reviewer pages :return: None or raises an assertion @@ -1393,16 +1570,20 @@ def test_reviewer_user_for_assignment_required_decorator_blocks_inactive_users(s decorated_func = decorators.reviewer_user_for_assignment_required(func) request = self.prepare_request_with_user(self.inactive_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows inactive users to access " - "production content") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows inactive users to access " + "production content", + ) - def test_reviewer_user_for_assignment_required_decorator_blocks_anonymous_users(self): + def test_reviewer_user_for_assignment_required_decorator_blocks_anonymous_users( + self, + ): """ Tests that the reviewer_user_for_assignment_required decorator blocks anonymous users. :return: None or raises an assertion @@ -1412,17 +1593,21 @@ def test_reviewer_user_for_assignment_required_decorator_blocks_anonymous_users( anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'assignment_id': self.review_assignment.id} + kwargs = {"assignment_id": self.review_assignment.id} self.assertIsInstance(decorated_func(request, **kwargs), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "reviewer_user_for_assignment_required decorator wrongly allows anonymous users to access " - "production content") + self.assertFalse( + func.called, + "reviewer_user_for_assignment_required decorator wrongly allows anonymous users to access " + "production content", + ) # Tests for article_stage_production_required - def test_article_stage_production_required_blocks_access_to_published_articles(self): + def test_article_stage_production_required_blocks_access_to_published_articles( + self, + ): """ Tests that users with access to articles in production cannot access published articles. :return: None or raises an assertion @@ -1432,18 +1617,22 @@ def test_article_stage_production_required_blocks_access_to_published_articles(s anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_published.pk} + kwargs = {"article_id": self.article_published.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that have been" - "published") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that have been" + "published", + ) - def test_article_stage_production_required_blocks_access_to_unsubmitted_articles(self): + def test_article_stage_production_required_blocks_access_to_unsubmitted_articles( + self, + ): """ Tests that users with access to articles in production cannot access unsubmitted articles. :return: None or raises an assertion @@ -1453,18 +1642,22 @@ def test_article_stage_production_required_blocks_access_to_unsubmitted_articles anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_unsubmitted.pk} + kwargs = {"article_id": self.article_unsubmitted.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "unsubmitted") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "unsubmitted", + ) - def test_article_stage_production_required_blocks_access_to_unassigned_articles(self): + def test_article_stage_production_required_blocks_access_to_unassigned_articles( + self, + ): """ Tests that users with access to articles in production cannot access unassigned articles. :return: None or raises an assertion @@ -1474,16 +1667,18 @@ def test_article_stage_production_required_blocks_access_to_unassigned_articles( anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_unassigned.pk} + kwargs = {"article_id": self.article_unassigned.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "unassigned") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "unassigned", + ) def test_article_stage_production_required_blocks_access_to_assigned_articles(self): """ @@ -1495,18 +1690,22 @@ def test_article_stage_production_required_blocks_access_to_assigned_articles(se anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_assigned.pk} + kwargs = {"article_id": self.article_assigned.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "assigned") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "assigned", + ) - def test_article_stage_production_required_blocks_access_to_under_review_articles(self): + def test_article_stage_production_required_blocks_access_to_under_review_articles( + self, + ): """ Tests that users with access to articles in production cannot access articles that are under review. :return: None or raises an assertion @@ -1516,18 +1715,22 @@ def test_article_stage_production_required_blocks_access_to_under_review_article anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_under_review.pk} + kwargs = {"article_id": self.article_under_review.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "under review") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "under review", + ) - def test_article_stage_production_required_blocks_access_to_under_revision_articles(self): + def test_article_stage_production_required_blocks_access_to_under_revision_articles( + self, + ): """ Tests that users with access to articles in production cannot access articles that are under revision. :return: None or raises an assertion @@ -1537,16 +1740,18 @@ def test_article_stage_production_required_blocks_access_to_under_revision_artic anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_under_revision.pk} + kwargs = {"article_id": self.article_under_revision.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "under revision") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "under revision", + ) def test_article_stage_production_required_blocks_access_to_rejected_articles(self): """ @@ -1558,16 +1763,18 @@ def test_article_stage_production_required_blocks_access_to_rejected_articles(se anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_rejected.pk} + kwargs = {"article_id": self.article_rejected.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "rejected") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "rejected", + ) def test_article_stage_production_required_blocks_access_to_accepted_articles(self): """ @@ -1579,18 +1786,22 @@ def test_article_stage_production_required_blocks_access_to_accepted_articles(se anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_accepted.pk} + kwargs = {"article_id": self.article_accepted.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "accepted") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "accepted", + ) - def test_article_stage_production_required_blocks_access_to_editor_copyediting_articles(self): + def test_article_stage_production_required_blocks_access_to_editor_copyediting_articles( + self, + ): """ Tests that users with access to articles in production cannot access articles that are in editor copyediting. :return: None or raises an assertion @@ -1600,18 +1811,22 @@ def test_article_stage_production_required_blocks_access_to_editor_copyediting_a anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_editor_copyediting.pk} + kwargs = {"article_id": self.article_editor_copyediting.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "in editor copyediting") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "in editor copyediting", + ) - def test_article_stage_production_required_blocks_access_to_author_copyediting_articles(self): + def test_article_stage_production_required_blocks_access_to_author_copyediting_articles( + self, + ): """ Tests that users with access to articles in production cannot access articles that are in author copyediting. :return: None or raises an assertion @@ -1621,18 +1836,22 @@ def test_article_stage_production_required_blocks_access_to_author_copyediting_a anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_author_copyediting.pk} + kwargs = {"article_id": self.article_author_copyediting.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "in author copyediting") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "in author copyediting", + ) - def test_article_stage_production_required_blocks_access_to_final_copyediting_articles(self): + def test_article_stage_production_required_blocks_access_to_final_copyediting_articles( + self, + ): """ Tests that users with access to articles in production cannot access articles that are in final copyediting. :return: None or raises an assertion @@ -1642,16 +1861,18 @@ def test_article_stage_production_required_blocks_access_to_final_copyediting_ar anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_final_copyediting.pk} + kwargs = {"article_id": self.article_final_copyediting.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "in final copyediting") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "in final copyediting", + ) def test_article_stage_production_required_blocks_access_to_proofing_articles(self): """ @@ -1663,18 +1884,22 @@ def test_article_stage_production_required_blocks_access_to_proofing_articles(se anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_proofing.pk} + kwargs = {"article_id": self.article_proofing.pk} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_production_required decorator wrongly allows access to articles that are" - "in proofing") + self.assertFalse( + func.called, + "article_stage_production_required decorator wrongly allows access to articles that are" + "in proofing", + ) - def test_article_stage_production_required_allows_access_to_production_articles(self): + def test_article_stage_production_required_allows_access_to_production_articles( + self, + ): """ Tests that users with access to articles in production can access articles that are in production. :return: None or raises an assertion @@ -1684,17 +1909,21 @@ def test_article_stage_production_required_allows_access_to_production_articles( anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_production_required decorator wrongly blocks access to articles that are in" - "production") + self.assertTrue( + func.called, + "article_stage_production_required decorator wrongly blocks access to articles that are in" + "production", + ) # Tests for article_stage_accepted_or_later_required - def test_article_stage_accepted_or_later_required_allows_access_to_published_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_published_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to published articles. :return: None or raises an assertion @@ -1704,17 +1933,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_published_art anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_published.identifier.identifier, - 'identifier_type': self.article_published.identifier.id_type} + kwargs = { + "identifier": self.article_published.identifier.identifier, + "identifier_type": self.article_published.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "published") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "published", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_unsubmitted_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_unsubmitted_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to unsubmitted articles. :return: None or raises an assertion @@ -1724,19 +1959,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_unsubmitted_a anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_unsubmitted.identifier.identifier, - 'identifier_type': self.article_unsubmitted.identifier.id_type} + kwargs = { + "identifier": self.article_unsubmitted.identifier.identifier, + "identifier_type": self.article_unsubmitted.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "unsubmitted") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "unsubmitted", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_unassigned_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_unassigned_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to unassigned articles. :return: None or raises an assertion @@ -1746,19 +1987,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_unassigned_ar anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_unassigned.identifier.identifier, - 'identifier_type': self.article_unassigned.identifier.id_type} + kwargs = { + "identifier": self.article_unassigned.identifier.identifier, + "identifier_type": self.article_unassigned.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "unassigned") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "unassigned", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_assigned_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_assigned_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to assigned articles. :return: None or raises an assertion @@ -1768,19 +2015,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_assigned_arti anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_assigned.identifier.identifier, - 'identifier_type': self.article_assigned.identifier.id_type} + kwargs = { + "identifier": self.article_assigned.identifier.identifier, + "identifier_type": self.article_assigned.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "assigned") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "assigned", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_under_review_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_under_review_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to articles that are under review. @@ -1791,19 +2044,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_under_review_ anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_under_review.identifier.identifier, - 'identifier_type': self.article_under_review.identifier.id_type} + kwargs = { + "identifier": self.article_under_review.identifier.identifier, + "identifier_type": self.article_under_review.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "under review") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "under review", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_under_revision_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_under_revision_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to articles that are under revision. @@ -1814,19 +2073,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_under_revisio anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_under_revision.identifier.identifier, - 'identifier_type': self.article_under_revision.identifier.id_type} + kwargs = { + "identifier": self.article_under_revision.identifier.identifier, + "identifier_type": self.article_under_revision.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "under revision") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "under revision", + ) - def test_article_stage_accepted_or_later_required_blocks_access_to_rejected_articles(self): + def test_article_stage_accepted_or_later_required_blocks_access_to_rejected_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to rejected articles. :return: None or raises an assertion @@ -1836,19 +2101,25 @@ def test_article_stage_accepted_or_later_required_blocks_access_to_rejected_arti anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_rejected.identifier.identifier, - 'identifier_type': self.article_rejected.identifier.id_type} + kwargs = { + "identifier": self.article_rejected.identifier.identifier, + "identifier_type": self.article_rejected.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "rejected") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "rejected", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_accepted_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_accepted_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to accepted articles. :return: None or raises an assertion @@ -1858,17 +2129,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_accepted_arti anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_accepted.identifier.identifier, - 'identifier_type': self.article_accepted.identifier.id_type} + kwargs = { + "identifier": self.article_accepted.identifier.identifier, + "identifier_type": self.article_accepted.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "accepted") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "accepted", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_editor_copyediting_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_editor_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to editor copyediting articles. @@ -1879,17 +2156,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_editor_copyed anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_editor_copyediting.identifier.identifier, - 'identifier_type': self.article_editor_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_editor_copyediting.identifier.identifier, + "identifier_type": self.article_editor_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in editor copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in editor copyediting", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_author_copyediting_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_author_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to author copyediting articles. @@ -1900,17 +2183,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_author_copyed anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_author_copyediting.identifier.identifier, - 'identifier_type': self.article_author_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_author_copyediting.identifier.identifier, + "identifier_type": self.article_author_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in author copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in author copyediting", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_final_copyediting_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_final_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to final copyediting articles. @@ -1921,17 +2210,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_final_copyedi anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_final_copyediting.identifier.identifier, - 'identifier_type': self.article_final_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_final_copyediting.identifier.identifier, + "identifier_type": self.article_final_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in final copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in final copyediting", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_proofing_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_proofing_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to proofing articles. :return: None or raises an assertion @@ -1941,17 +2236,23 @@ def test_article_stage_accepted_or_later_required_allows_access_to_proofing_arti anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_proofing.identifier.identifier, - 'identifier_type': self.article_proofing.identifier.id_type} + kwargs = { + "identifier": self.article_proofing.identifier.identifier, + "identifier_type": self.article_proofing.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in proofing") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in proofing", + ) - def test_article_stage_accepted_or_later_required_allows_access_to_production_articles(self): + def test_article_stage_accepted_or_later_required_allows_access_to_production_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to production articles. :return: None or raises an assertion @@ -1961,312 +2262,429 @@ def test_article_stage_accepted_or_later_required_allows_access_to_production_ar anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type} + kwargs = { + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in production") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in production", + ) # Tests for article_stage_accepted_or_later_or_staff_required - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_published_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_published_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to published articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_published.identifier.identifier, - 'identifier_type': self.article_published.identifier.id_type} + kwargs = { + "identifier": self.article_published.identifier.identifier, + "identifier_type": self.article_published.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "published") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "published", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_unsubmitted_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_unsubmitted_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to unsubmitted articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_unsubmitted.identifier.identifier, - 'identifier_type': self.article_unsubmitted.identifier.id_type} + kwargs = { + "identifier": self.article_unsubmitted.identifier.identifier, + "identifier_type": self.article_unsubmitted.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "unsubmitted") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "unsubmitted", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_unassigned_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_unassigned_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to unassigned articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_unassigned.identifier.identifier, - 'identifier_type': self.article_unassigned.identifier.id_type} + kwargs = { + "identifier": self.article_unassigned.identifier.identifier, + "identifier_type": self.article_unassigned.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "unassigned") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "unassigned", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_assigned_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_assigned_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to assigned articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_assigned.identifier.identifier, - 'identifier_type': self.article_assigned.identifier.id_type} + kwargs = { + "identifier": self.article_assigned.identifier.identifier, + "identifier_type": self.article_assigned.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "assigned") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "assigned", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_under_review_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_under_review_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to articles that are under review. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_under_review.identifier.identifier, - 'identifier_type': self.article_under_review.identifier.id_type} + kwargs = { + "identifier": self.article_under_review.identifier.identifier, + "identifier_type": self.article_under_review.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "under review") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "under review", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_under_revision_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_under_revision_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to articles that are under revision. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_under_revision.identifier.identifier, - 'identifier_type': self.article_under_revision.identifier.id_type} + kwargs = { + "identifier": self.article_under_revision.identifier.identifier, + "identifier_type": self.article_under_revision.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "under revision") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "under revision", + ) - def test_article_stage_accepted_or_later_or_staff_allows_staff_access_to_rejected_articles(self): + def test_article_stage_accepted_or_later_or_staff_allows_staff_access_to_rejected_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows staff access to rejected articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'identifier': self.article_rejected.identifier.identifier, - 'identifier_type': self.article_rejected.identifier.id_type} + kwargs = { + "identifier": self.article_rejected.identifier.identifier, + "identifier_type": self.article_rejected.identifier.id_type, + } # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "article_stage_accepted_or_later_or_staff_required decorator wrongly blocks staff " - "access to articles that are rejected") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_or_staff_required decorator wrongly blocks staff " + "access to articles that are rejected", + ) - def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_rejected_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_blocks_access_to_rejected_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance blocks access to rejected articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_rejected.identifier.identifier, - 'identifier_type': self.article_rejected.identifier.id_type} + kwargs = { + "identifier": self.article_rejected.identifier.identifier, + "identifier_type": self.article_rejected.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" - "rejected") + self.assertFalse( + func.called, + "article_stage_accepted_or_later_required decorator wrongly allows access to articles that are" + "rejected", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_accepted_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_accepted_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to accepted articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_accepted.identifier.identifier, - 'identifier_type': self.article_accepted.identifier.id_type} + kwargs = { + "identifier": self.article_accepted.identifier.identifier, + "identifier_type": self.article_accepted.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "accepted") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "accepted", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_editor_copyediting_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_editor_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to editor copyediting articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_editor_copyediting.identifier.identifier, - 'identifier_type': self.article_editor_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_editor_copyediting.identifier.identifier, + "identifier_type": self.article_editor_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in editor copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in editor copyediting", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_author_copyediting_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_author_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to author copyediting articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_author_copyediting.identifier.identifier, - 'identifier_type': self.article_author_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_author_copyediting.identifier.identifier, + "identifier_type": self.article_author_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in author copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in author copyediting", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_final_copyediting_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_final_copyediting_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to final copyediting articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_final_copyediting.identifier.identifier, - 'identifier_type': self.article_final_copyediting.identifier.id_type} + kwargs = { + "identifier": self.article_final_copyediting.identifier.identifier, + "identifier_type": self.article_final_copyediting.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in final copyediting") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in final copyediting", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_proofing_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_proofing_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to proofing articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_proofing.identifier.identifier, - 'identifier_type': self.article_proofing.identifier.id_type} + kwargs = { + "identifier": self.article_proofing.identifier.identifier, + "identifier_type": self.article_proofing.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in proofing") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in proofing", + ) - def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_production_articles(self): + def test_article_stage_accepted_or_later_or_staff_required_allows_access_to_production_articles( + self, + ): """ Tests that the function that checks if an article is post-acceptance allows access to production articles. :return: None or raises an assertion """ func = Mock() - decorated_func = decorators.article_stage_accepted_or_later_or_staff_required(func) + decorated_func = decorators.article_stage_accepted_or_later_or_staff_required( + func + ) anon_user = AnonymousUser() request = self.prepare_request_with_user(anon_user, self.journal_one) - kwargs = {'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type} + kwargs = { + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, - "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" - "in production") + self.assertTrue( + func.called, + "article_stage_accepted_or_later_required decorator wrongly blocks access to articles that are" + "in production", + ) # Tests for article_edit_user_required def test_article_edit_user_required_allows_access_to_production_articles(self): @@ -2278,15 +2696,20 @@ def test_article_edit_user_required_allows_access_to_production_articles(self): decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.id} + kwargs = {"article_id": self.article_in_production.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, "article_edit_user_required decorator wrongly blocks access to articles that are " - "in production") + self.assertTrue( + func.called, + "article_edit_user_required decorator wrongly blocks access to articles that are " + "in production", + ) - def test_article_edit_user_required_allows_staff_access_to_production_articles(self): + def test_article_edit_user_required_allows_staff_access_to_production_articles( + self, + ): """ Tests that a staff user can edit a specific article in the production stage :return: None or raises an assertion @@ -2295,13 +2718,16 @@ def test_article_edit_user_required_allows_staff_access_to_production_articles(s decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.id} + kwargs = {"article_id": self.article_in_production.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, "article_edit_user_required decorator wrongly blocks staff access to articles that" - " are in production") + self.assertTrue( + func.called, + "article_edit_user_required decorator wrongly blocks staff access to articles that" + " are in production", + ) def test_article_edit_user_required_allows_staff_access_to_published_articles(self): """ @@ -2312,13 +2738,16 @@ def test_article_edit_user_required_allows_staff_access_to_published_articles(se decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'article_id': self.article_published.id} + kwargs = {"article_id": self.article_published.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, "article_edit_user_required decorator wrongly blocks staff access to articles that" - " are published") + self.assertTrue( + func.called, + "article_edit_user_required decorator wrongly blocks staff access to articles that" + " are published", + ) def test_article_edit_user_required_allows_staff_access_to_rejected_articles(self): """ @@ -2329,15 +2758,20 @@ def test_article_edit_user_required_allows_staff_access_to_rejected_articles(sel decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'article_id': self.article_rejected.id} + kwargs = {"article_id": self.article_rejected.id} decorated_func(request, **kwargs) # test that the callback was called - self.assertTrue(func.called, "article_edit_user_required decorator wrongly blocks staff access to articles that" - " are rejected") + self.assertTrue( + func.called, + "article_edit_user_required decorator wrongly blocks staff access to articles that" + " are rejected", + ) - def test_article_edit_user_required_blocks_access_to_production_articles_if_no_user(self): + def test_article_edit_user_required_blocks_access_to_production_articles_if_no_user( + self, + ): """ Tests that an anonymous user cannot edit a specific article in the production stage :return: None or raises an assertion @@ -2346,16 +2780,18 @@ def test_article_edit_user_required_blocks_access_to_production_articles_if_no_u decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(AnonymousUser(), self.journal_one) - kwargs = {'article_id': self.article_published.id} + kwargs = {"article_id": self.article_published.id} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_edit_user_required decorator wrongly allows anonymous access to articles that are " - "in production") + self.assertFalse( + func.called, + "article_edit_user_required decorator wrongly allows anonymous access to articles that are " + "in production", + ) def test_article_edit_user_required_blocks_access_to_articles_if_not_owner(self): """ @@ -2366,16 +2802,18 @@ def test_article_edit_user_required_blocks_access_to_articles_if_not_owner(self) decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.second_user, self.journal_one) - kwargs = {'article_id': self.article_in_production.id} + kwargs = {"article_id": self.article_in_production.id} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_edit_user_required decorator wrongly allows access to articles that are " - "in production and not owned by the current user") + self.assertFalse( + func.called, + "article_edit_user_required decorator wrongly allows access to articles that are " + "in production and not owned by the current user", + ) def test_article_edit_user_required_blocks_access_to_published_articles(self): """ @@ -2386,16 +2824,18 @@ def test_article_edit_user_required_blocks_access_to_published_articles(self): decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'article_id': self.article_published.id} + kwargs = {"article_id": self.article_published.id} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_edit_user_required decorator wrongly allows access to articles that are " - "published") + self.assertFalse( + func.called, + "article_edit_user_required decorator wrongly allows access to articles that are " + "published", + ) def test_article_edit_user_required_blocks_access_to_rejected_articles(self): """ @@ -2406,16 +2846,18 @@ def test_article_edit_user_required_blocks_access_to_rejected_articles(self): decorated_func = decorators.article_edit_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'article_id': self.article_rejected.id} + kwargs = {"article_id": self.article_rejected.id} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, - "article_edit_user_required decorator wrongly allows access to articles that are " - "rejected") + self.assertFalse( + func.called, + "article_edit_user_required decorator wrongly allows access to articles that are " + "rejected", + ) # Tests for file_user_required def test_file_user_required_blocks_access_to_anonymous_users_to_private_files(self): @@ -2427,15 +2869,18 @@ def test_file_user_required_blocks_access_to_anonymous_users_to_private_files(se decorated_func = decorators.file_user_required(func) request = self.prepare_request_with_user(AnonymousUser(), self.journal_one) - kwargs = {'file_id': self.private_file.id} + kwargs = {"file_id": self.private_file.id} with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "file_user_required is erroneously allowing anonymous users access to private " - "files") + self.assertFalse( + func.called, + "file_user_required is erroneously allowing anonymous users access to private " + "files", + ) def test_file_user_required_allows_access_to_anonymous_users_to_public_files(self): """ @@ -2446,13 +2891,16 @@ def test_file_user_required_allows_access_to_anonymous_users_to_public_files(sel decorated_func = decorators.file_user_required(func) request = self.prepare_request_with_user(AnonymousUser(), self.journal_one) - kwargs = {'file_id': self.public_file.id} + kwargs = {"file_id": self.public_file.id} decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "file_user_required is erroneously blocking anonymous users access to public " - "files") + self.assertTrue( + func.called, + "file_user_required is erroneously blocking anonymous users access to public " + "files", + ) def test_file_user_required_allows_access_to_owners_to_private_files(self): """ @@ -2463,12 +2911,15 @@ def test_file_user_required_allows_access_to_owners_to_private_files(self): decorated_func = decorators.file_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'file_id': self.private_file.id} + kwargs = {"file_id": self.private_file.id} decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "file_user_required is erroneously blocking owner users access to private files") + self.assertTrue( + func.called, + "file_user_required is erroneously blocking owner users access to private files", + ) def test_file_user_required_allows_staff_access_to_private_files(self): """ @@ -2479,12 +2930,15 @@ def test_file_user_required_allows_staff_access_to_private_files(self): decorated_func = decorators.file_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id} + kwargs = {"file_id": self.private_file.id} decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "file_user_required is erroneously blocking staff users access to private files") + self.assertTrue( + func.called, + "file_user_required is erroneously blocking staff users access to private files", + ) # Tests for file_edit_user_required def test_file_edit_user_required_blocks_access_to_anonymous_users(self): @@ -2496,17 +2950,21 @@ def test_file_edit_user_required_blocks_access_to_anonymous_users(self): decorated_func = decorators.file_edit_user_required(func) request = self.prepare_request_with_user(AnonymousUser(), self.journal_one) - kwargs = {'file_id': self.public_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.public_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that production_user_or_editor_required raises a PermissionDenied exception decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "file_user_required is erroneously allowing anonymous users edit access to files") + self.assertFalse( + func.called, + "file_user_required is erroneously allowing anonymous users edit access to files", + ) def test_file_edit_user_required_allows_access_to_owners_to_private_files(self): """ @@ -2517,15 +2975,19 @@ def test_file_edit_user_required_allows_access_to_owners_to_private_files(self): decorated_func = decorators.file_edit_user_required(func) request = self.prepare_request_with_user(self.regular_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "file_user_required is erroneously blocking owner users access to private files") + self.assertTrue( + func.called, + "file_user_required is erroneously blocking owner users access to private files", + ) def test_file_edit_user_required_blocks_access_to_non_owners_to_files(self): """ @@ -2536,17 +2998,21 @@ def test_file_edit_user_required_blocks_access_to_non_owners_to_files(self): decorated_func = decorators.file_edit_user_required(func) request = self.prepare_request_with_user(self.second_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that this throws a PermissionDenied error decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "file_user_required is erroneously allowing non-owner users edit access to files") + self.assertFalse( + func.called, + "file_user_required is erroneously allowing non-owner users edit access to files", + ) def test_file_edit_user_required_allows_staff_access_to_files(self): """ @@ -2557,15 +3023,19 @@ def test_file_edit_user_required_allows_staff_access_to_files(self): decorated_func = decorators.file_edit_user_required(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "file_user_required is erroneously blocking staff users access to private files") + self.assertTrue( + func.called, + "file_user_required is erroneously blocking staff users access to private files", + ) # Test for data_figure_file def test_data_figure_file_correctly_identifies_data_figure_file(self): @@ -2577,15 +3047,19 @@ def test_data_figure_file_correctly_identifies_data_figure_file(self): decorated_func = decorators.data_figure_file(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.public_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.public_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "data_figure_file is not correctly identifying data/figure files") + self.assertTrue( + func.called, + "data_figure_file is not correctly identifying data/figure files", + ) def test_data_figure_file_does_not_falsely_identify_data_figure_file(self): """ @@ -2596,17 +3070,20 @@ def test_data_figure_file_does_not_falsely_identify_data_figure_file(self): decorated_func = decorators.data_figure_file(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } with self.assertRaises(PermissionDenied): # test that this throws a PermissionDenied error decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "data_figure_file is falsely identifying data/figure files") + self.assertFalse( + func.called, "data_figure_file is falsely identifying data/figure files" + ) # Tests for has_request def test_has_request_works_when_request_present(self): @@ -2618,10 +3095,11 @@ def test_has_request_works_when_request_present(self): decorated_func = decorators.has_request(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) @@ -2641,7 +3119,9 @@ def test_has_request_fails_when_request_absent(self): decorated_func(None, None) # test that the callback was not called - self.assertFalse(func.called, "has_request is succeeded even when a request is absent") + self.assertFalse( + func.called, "has_request is succeeded even when a request is absent" + ) # Tests for has_journal def test_has_journal_works_when_journal_present(self): @@ -2653,10 +3133,11 @@ def test_has_journal_works_when_journal_present(self): decorated_func = decorators.has_journal(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) @@ -2672,10 +3153,11 @@ def test_has_journal_fails_when_journal_absent(self): decorated_func = decorators.has_journal(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } request.journal = None @@ -2684,7 +3166,9 @@ def test_has_journal_fails_when_journal_absent(self): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "has_journal is succeeded even when a journal is absent") + self.assertFalse( + func.called, "has_journal is succeeded even when a journal is absent" + ) # Tests for article_exists def test_article_exists_works_when_article_exists(self): @@ -2696,15 +3180,18 @@ def test_article_exists_works_when_article_exists(self): decorated_func = decorators.article_exists(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': self.article_in_production.identifier.identifier, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": self.article_in_production.identifier.identifier, + "identifier_type": self.article_in_production.identifier.id_type, + } decorated_func(request, **kwargs) # test that the callback was not called - self.assertTrue(func.called, "article_exists is failing when the article exists") + self.assertTrue( + func.called, "article_exists is failing when the article exists" + ) def test_article_exists_fails_when_article_does_not_exist(self): """ @@ -2715,10 +3202,11 @@ def test_article_exists_fails_when_article_does_not_exist(self): decorated_func = decorators.has_journal(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs = {'file_id': self.private_file.id, - 'identifier': 1337, - 'identifier_type': self.article_in_production.identifier.id_type - } + kwargs = { + "file_id": self.private_file.id, + "identifier": 1337, + "identifier_type": self.article_in_production.identifier.id_type, + } request.journal = None @@ -2727,7 +3215,10 @@ def test_article_exists_fails_when_article_does_not_exist(self): decorated_func(request, **kwargs) # test that the callback was not called - self.assertFalse(func.called, "article_exists is erroneously succeeding when the article does not exist") + self.assertFalse( + func.called, + "article_exists is erroneously succeeding when the article does not exist", + ) def test_article_decision_not_made(self): """ @@ -2739,10 +3230,12 @@ def test_article_decision_not_made(self): decorated_func = decorators.article_decision_not_made(func) request = self.prepare_request_with_user(self.admin_user, self.journal_one) - kwargs_article_id = {'article_id': self.article_review_completed.id} - kwargs_review_id = {'review_id': self.review_assignment_complete.id} + kwargs_article_id = {"article_id": self.article_review_completed.id} + kwargs_review_id = {"review_id": self.review_assignment_complete.id} - expected_redirect_url = reverse('review_in_review', kwargs={'article_id': self.article_review_completed.id}) + expected_redirect_url = reverse( + "review_in_review", kwargs={"article_id": self.article_review_completed.id} + ) article_test = decorated_func(request, **kwargs_article_id).url review_test = decorated_func(request, **kwargs_review_id).url @@ -2759,10 +3252,10 @@ def test_article_author_required_with_author(self): decorated_func = decorators.article_author_required(func) request = self.prepare_request_with_user(self.author, self.journal_one) - kwargs_article_id = {'article_id': self.article_author_is_owner.id} + kwargs_article_id = {"article_id": self.article_author_is_owner.id} decorated_func(request, **kwargs_article_id) - self.assertTrue(func.called, 'User is an author') + self.assertTrue(func.called, "User is an author") # Test with user who does not have role @@ -2771,7 +3264,7 @@ def test_article_author_required_with_wrong_author(self): decorated_func = decorators.article_author_required(func) request = self.prepare_request_with_user(self.production, self.journal_one) - kwargs_article_id = {'article_id': self.article_author_is_owner.id} + kwargs_article_id = {"article_id": self.article_author_is_owner.id} with self.assertRaises(PermissionDenied): # test that this throws a PermissionDenied error @@ -2787,8 +3280,10 @@ def test_copyeditor_user_required(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "copyeditor_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "copyeditor_user_required decorator wrongly prohibits staff from accessing content", + ) def test_copyeditor_user_required_allows_staff(self): func = Mock() @@ -2800,8 +3295,10 @@ def test_copyeditor_user_required_allows_staff(self): decorated_func(request) # test that the callback was called - self.assertTrue(func.called, - "copyeditor_user_required decorator wrongly prohibits staff from accessing content") + self.assertTrue( + func.called, + "copyeditor_user_required decorator wrongly prohibits staff from accessing content", + ) def test_copyeditor_user_required_with_null_user(self): func = Mock() @@ -2824,19 +3321,21 @@ def test_copyeditor_user_required_blocks_non_copyeditor(self): def test_copyeditor_for_copyedit(self): func = Mock() decorated_func = decorators.copyeditor_for_copyedit_required(func) - kwargs = {'copyedit_id': self.copyedit_assignment.pk} + kwargs = {"copyedit_id": self.copyedit_assignment.pk} request = self.prepare_request_with_user(self.copyeditor, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "copyeditor_for_copyedit_required wrongly prohibits copyeditor from accessing content") + self.assertTrue( + func.called, + "copyeditor_for_copyedit_required wrongly prohibits copyeditor from accessing content", + ) def test_copyeditor_for_copyedit_with_author(self): func = Mock() decorated_func = decorators.copyeditor_for_copyedit_required(func) - kwargs = {'copyedit_id': self.copyedit_assignment.pk} + kwargs = {"copyedit_id": self.copyedit_assignment.pk} request = self.prepare_request_with_user(self.author, self.journal_one) @@ -2846,30 +3345,34 @@ def test_copyeditor_for_copyedit_with_author(self): def test_copyeditor_for_copyedit_with_staff(self): func = Mock() decorated_func = decorators.copyeditor_for_copyedit_required(func) - kwargs = {'copyedit_id': self.copyedit_assignment.pk} + kwargs = {"copyedit_id": self.copyedit_assignment.pk} request = self.prepare_request_with_user(self.admin_user, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "copyeditor_for_copyedit_required wrongly prohibits admin from accessing content") + self.assertTrue( + func.called, + "copyeditor_for_copyedit_required wrongly prohibits admin from accessing content", + ) def test_typesetter_user_required_with_typesetter(self): func = Mock() decorated_func = decorators.typesetter_user_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} request = self.prepare_request_with_user(self.typesetter, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "typesetter_user_required wrongly prohibits typesetter from accessing content") + self.assertTrue( + func.called, + "typesetter_user_required wrongly prohibits typesetter from accessing content", + ) def test_typesetter_user_required_with_copyeditor(self): func = Mock() decorated_func = decorators.typesetter_user_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} request = self.prepare_request_with_user(self.copyeditor, self.journal_one) @@ -2879,18 +3382,20 @@ def test_typesetter_user_required_with_copyeditor(self): def test_typesetter_or_editor_with_typesetter(self): func = Mock() decorated_func = decorators.typesetter_or_editor_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} request = self.prepare_request_with_user(self.typesetter, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "typesetter_user_required wrongly prohibits typesetter from accessing content") + self.assertTrue( + func.called, + "typesetter_user_required wrongly prohibits typesetter from accessing content", + ) def test_typesetter_or_editor_with_copyeditor(self): func = Mock() decorated_func = decorators.typesetter_or_editor_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} request = self.prepare_request_with_user(self.copyeditor, self.journal_one) @@ -2900,9 +3405,11 @@ def test_typesetter_or_editor_with_copyeditor(self): def test_typesetter_or_editor_with_other_typesetter(self): func = Mock() decorated_func = decorators.typesetter_or_editor_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} - request = self.prepare_request_with_user(self.other_typesetter, self.journal_one) + request = self.prepare_request_with_user( + self.other_typesetter, self.journal_one + ) with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) @@ -2910,23 +3417,29 @@ def test_typesetter_or_editor_with_other_typesetter(self): def test_typesetter_or_editor_with_other_editor(self): func = Mock() decorated_func = decorators.typesetter_or_editor_required(func) - kwargs = {'typeset_id': self.typeset_task.pk} + kwargs = {"typeset_id": self.typeset_task.pk} request = self.prepare_request_with_user(self.editor, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "typesetter_or_editor wrongly prohibits editor from accessing content") + self.assertTrue( + func.called, + "typesetter_or_editor wrongly prohibits editor from accessing content", + ) def test_proofing_manager_or_editor_required_with_proofing_manager(self): func = Mock() decorated_func = decorators.proofing_manager_or_editor_required(func) - request = self.prepare_request_with_user(self.proofing_manager, self.journal_one) + request = self.prepare_request_with_user( + self.proofing_manager, self.journal_one + ) decorated_func(request) - self.assertTrue(func.called, - "proofing_manager_or_editor_required, wrongly prohibits manager or editor from content") + self.assertTrue( + func.called, + "proofing_manager_or_editor_required, wrongly prohibits manager or editor from content", + ) def test_proofing_manager_or_editor_required_with_typesetter(self): func = Mock() @@ -2940,18 +3453,22 @@ def test_proofing_manager_or_editor_required_with_typesetter(self): def test_proofing_manager_for_article_required(self): func = Mock() decorated_func = decorators.proofing_manager_for_article_required(func) - kwargs = {'article_id': self.article_proofing.pk} + kwargs = {"article_id": self.article_proofing.pk} - request = self.prepare_request_with_user(self.proofing_manager, self.journal_one) + request = self.prepare_request_with_user( + self.proofing_manager, self.journal_one + ) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "proofing_manager_for_article_required, wrongly prohibits manager or editor from content") + self.assertTrue( + func.called, + "proofing_manager_for_article_required, wrongly prohibits manager or editor from content", + ) def test_proofing_manager_for_article_required_without_manager(self): func = Mock() decorated_func = decorators.proofing_manager_for_article_required(func) - kwargs = {'article_id': self.article_proofing.pk} + kwargs = {"article_id": self.article_proofing.pk} request = self.prepare_request_with_user(self.regular_user, self.journal_one) @@ -2966,12 +3483,16 @@ def test_proofreader_or_typesetter_required(self): request_two = self.prepare_request_with_user(self.typesetter, self.journal_one) decorated_func(request_one) - self.assertTrue(func.called, - "proofreader_or_typesetter_required, wrongly prohibits proofreader or editor from content") + self.assertTrue( + func.called, + "proofreader_or_typesetter_required, wrongly prohibits proofreader or editor from content", + ) decorated_func(request_two) - self.assertTrue(func.called, - "proofreader_or_typesetter_required, wrongly prohibits typesetter or editor from content") + self.assertTrue( + func.called, + "proofreader_or_typesetter_required, wrongly prohibits typesetter or editor from content", + ) def test_proofreader_or_typesetter_required_with_copyeditor(self): func = Mock() @@ -2985,40 +3506,45 @@ def test_proofreader_or_typesetter_required_with_copyeditor(self): def test_proofreader_for_article_required(self): func = Mock() decorated_func = decorators.proofreader_for_article_required(func) - kwargs = {'proofing_task_id': self.proofing_task.pk} + kwargs = {"proofing_task_id": self.proofing_task.pk} request = self.prepare_request_with_user(self.proofreader, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "proofreader_for_article_required, wrongly prohibits proofreader from content") + self.assertTrue( + func.called, + "proofreader_for_article_required, wrongly prohibits proofreader from content", + ) def test_proofreader_for_article_required_with_author_proofreader(self): func = Mock() decorated_func = decorators.proofreader_for_article_required(func) author_proofing_task = proofing_models.ProofingTask( - round=self.proofing_assignment.current_proofing_round(), - proofreader=self.author, - notified=True, - due=timezone.now(), - accepted=timezone.now(), - task='author_task') + round=self.proofing_assignment.current_proofing_round(), + proofreader=self.author, + notified=True, + due=timezone.now(), + accepted=timezone.now(), + task="author_task", + ) author_proofing_task.save() - kwargs = {'proofing_task_id': author_proofing_task.pk} + kwargs = {"proofing_task_id": author_proofing_task.pk} request = self.prepare_request_with_user(self.author, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "proofreader_for_article_required, wrongly prohibits " - "author proofreader from content") + self.assertTrue( + func.called, + "proofreader_for_article_required, wrongly prohibits " + "author proofreader from content", + ) def test_proofreader_for_article_required_with_bad_proofreader(self): func = Mock() decorated_func = decorators.proofreader_for_article_required(func) - kwargs = {'proofing_task_id': self.proofing_task.pk} + kwargs = {"proofing_task_id": self.proofing_task.pk} request = self.prepare_request_with_user(self.proofreader_two, self.journal_one) @@ -3028,20 +3554,24 @@ def test_proofreader_for_article_required_with_bad_proofreader(self): def test_typesetter_for_corrections_required(self): func = Mock() decorated_func = decorators.typesetter_for_corrections_required(func) - kwargs = {'typeset_task_id': self.correction_task.pk} + kwargs = {"typeset_task_id": self.correction_task.pk} request = self.prepare_request_with_user(self.typesetter, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "typesetter_for_corrections_required, wrongly prohibits typesetter from content") + self.assertTrue( + func.called, + "typesetter_for_corrections_required, wrongly prohibits typesetter from content", + ) def test_typesetter_for_corrections_required_with_bad_typesetter(self): func = Mock() decorated_func = decorators.typesetter_for_corrections_required(func) - kwargs = {'typeset_task_id': self.correction_task.pk} + kwargs = {"typeset_task_id": self.correction_task.pk} - request = self.prepare_request_with_user(self.other_typesetter, self.journal_one) + request = self.prepare_request_with_user( + self.other_typesetter, self.journal_one + ) with self.assertRaises(PermissionDenied): decorated_func(request, **kwargs) @@ -3049,13 +3579,15 @@ def test_typesetter_for_corrections_required_with_bad_typesetter(self): def test_proofreader_can_download_file(self): func = Mock() decorated_func = decorators.proofreader_for_article_required(func) - kwargs = {'proofing_task_id': self.proofing_task.pk, 'file_id': self.third_file.pk} + kwargs = { + "proofing_task_id": self.proofing_task.pk, + "file_id": self.third_file.pk, + } request = self.prepare_request_with_user(self.proofreader, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "proofreader cannot download proofing file...") + self.assertTrue(func.called, "proofreader cannot download proofing file...") def test_bad_user_cant_download_file(self): request = self.prepare_request_with_user(self.regular_user, self.journal_one) @@ -3065,39 +3597,42 @@ def test_bad_user_cant_download_file(self): def test_editor_is_author(self): func = Mock() decorated_func = decorators.editor_is_not_author(func) - kwargs = {'article_id': self.article_author_is_owner.pk} + kwargs = {"article_id": self.article_author_is_owner.pk} request = self.prepare_request_with_user(self.editor, self.journal_one) response = decorated_func(request, **kwargs) - expected_path = '/review/article/{0}/decision/review/access_denied/'.format( - self.article_author_is_owner.pk) + expected_path = "/review/article/{0}/decision/review/access_denied/".format( + self.article_author_is_owner.pk + ) self.assertTrue(response.url.endswith(expected_path)) def test_editor_is_not_author(self): func = Mock() decorated_func = decorators.editor_is_not_author(func) - kwargs = {'article_id': self.article_in_production.pk} + kwargs = {"article_id": self.article_in_production.pk} request = self.prepare_request_with_user(self.editor, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "editor_is_not_author wrongly blocks editor from content") + self.assertTrue( + func.called, "editor_is_not_author wrongly blocks editor from content" + ) def test_section_editor_can_access_assigned_article(self): func = Mock() decorated_func = decorators.editor_user_required(func) - kwargs = {'article_id': self.article_assigned.pk} + kwargs = {"article_id": self.article_assigned.pk} request = self.prepare_request_with_user(self.section_editor, self.journal_one) decorated_func(request, **kwargs) - self.assertTrue(func.called, - "editor_user_required wrongly blocks section editors") + self.assertTrue( + func.called, "editor_user_required wrongly blocks section editors" + ) def test_section_editor_cant_access_random_article(self): func = Mock() decorated_func = decorators.editor_user_required(func) - kwargs = {'article_id': self.article_author_copyediting.pk} + kwargs = {"article_id": self.article_author_copyediting.pk} request = self.prepare_request_with_user(self.section_editor, self.journal_one) @@ -3108,12 +3643,17 @@ def test_section_editor_cant_access_view_because_of_pii(self): func = Mock() with context_managers.janeway_setting_override( - "permission", "se_pii_filter", self.journal_one, True, + "permission", + "se_pii_filter", + self.journal_one, + True, ): decorated_func = decorators.editor_user_required_and_can_see_pii(func) - kwargs = {'article_id': self.article_in_review.pk} + kwargs = {"article_id": self.article_in_review.pk} - request = self.prepare_request_with_user(self.section_editor, self.journal_one) + request = self.prepare_request_with_user( + self.section_editor, self.journal_one + ) with self.assertRaises( PermissionDenied, @@ -3124,20 +3664,20 @@ def test_section_editor_cant_access_view_because_of_pii(self): def test_article_stage_review_required_with_review_article(self): func = Mock() decorated_func = decorators.article_stage_review_required(func) - kwargs = {'article_id': self.article_under_review.pk} + kwargs = {"article_id": self.article_under_review.pk} request = self.prepare_request_with_user(self.editor, None) decorated_func(request, **kwargs) self.assertTrue( func.called, - "article_stage_review_required wrongly blocks article in review" + "article_stage_review_required wrongly blocks article in review", ) def test_article_stage_review_required_with_bad_article(self): func = Mock() decorated_func = decorators.article_stage_review_required(func) - kwargs = {'article_id': self.article_author_copyediting.pk} + kwargs = {"article_id": self.article_author_copyediting.pk} request = self.prepare_request_with_user(self.editor, None) @@ -3222,7 +3762,7 @@ def test_production_user_or_editor_required_section_editor(self): func = Mock() decorated_func = decorators.production_user_or_editor_required(func) kwargs = { - 'article_id': self.article_in_production.pk, + "article_id": self.article_in_production.pk, } success_request = self.prepare_request_with_user( @@ -3251,7 +3791,7 @@ def test_section_editor_production_no_or_bad_article_id(self): decorated_func = decorators.production_user_or_editor_required(func) no_kwargs = {} bad_kwargs = { - 'article_id': self.article_unassigned.pk, + "article_id": self.article_unassigned.pk, } request = self.prepare_request_with_user( @@ -3283,10 +3823,10 @@ def test_loading_keyword_page_success(self): decorated_func = decorators.keyword_page_enabled(func) setting_handler.save_setting( - 'general', - 'keyword_list_page', + "general", + "keyword_list_page", self.journal_one, - 'on', + "on", ) request = self.prepare_request_with_user( @@ -3299,10 +3839,10 @@ def test_loading_keyword_page_success(self): # Negate any database changes on keepdb input setting_handler.save_setting( - 'general', - 'keyword_list_page', - self.journal_one, - '', + "general", + "keyword_list_page", + self.journal_one, + "", ) self.assertTrue( @@ -3313,7 +3853,7 @@ def test_loading_keyword_page_success(self): def test_preprint_editor_or_author_required_authorised(self): func = Mock() decorated_func = decorators.preprint_editor_or_author_required(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.editor, @@ -3323,13 +3863,13 @@ def test_preprint_editor_or_author_required_authorised(self): self.assertTrue( func.called, - "preprint_editor_or_author_required wrongly blocks editor from accessing preprints" + "preprint_editor_or_author_required wrongly blocks editor from accessing preprints", ) def test_preprint_editor_or_author_required_author(self): func = Mock() decorated_func = decorators.preprint_editor_or_author_required(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.author, @@ -3339,13 +3879,13 @@ def test_preprint_editor_or_author_required_author(self): self.assertTrue( func.called, - "preprint_editor_or_author_required wrongly blocks author from accessing preprints" + "preprint_editor_or_author_required wrongly blocks author from accessing preprints", ) def test_preprint_editor_or_author_required_unauthorised(self): func = Mock() decorated_func = decorators.preprint_editor_or_author_required(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.proofreader, @@ -3358,7 +3898,7 @@ def test_preprint_editor_or_author_required_unauthorised(self): def test_is_article_preprint_editor_with_subject_editor(self): func = Mock() decorated_func = decorators.is_article_preprint_editor(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.proofing_manager, @@ -3368,13 +3908,13 @@ def test_is_article_preprint_editor_with_subject_editor(self): self.assertTrue( func.called, - "is_article_preprint_editor wrongly blocks subject editor from accessing preprints" + "is_article_preprint_editor wrongly blocks subject editor from accessing preprints", ) def test_is_article_preprint_editor_with_bad_user(self): func = Mock() decorated_func = decorators.is_article_preprint_editor(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.section_editor, @@ -3387,7 +3927,7 @@ def test_is_article_preprint_editor_with_bad_user(self): def test_is_repository_manager(self): func = Mock() decorated_func = decorators.is_repository_manager(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.editor, @@ -3397,13 +3937,13 @@ def test_is_repository_manager(self): self.assertTrue( func.called, - "is_repository_manager wrongly blocks subject editor from accessing preprints" + "is_repository_manager wrongly blocks subject editor from accessing preprints", ) def test_is_repository_manager_with_bad_user(self): func = Mock() decorated_func = decorators.is_repository_manager(func) - kwargs = {'preprint_id': self.preprint.pk} + kwargs = {"preprint_id": self.preprint.pk} request = self.prepare_request_with_user( self.section_editor, @@ -3425,7 +3965,7 @@ def test_press_only(self): self.assertTrue( func.called, - "press_only incorrectly redirects when there is no journal or repo present in request" + "press_only incorrectly redirects when there is no journal or repo present in request", ) def test_press_only_with_journal(self): @@ -3437,8 +3977,10 @@ def test_press_only_with_journal(self): self.assertIsInstance(decorated_func(request), HttpResponseRedirect) # test that the callback was not called - self.assertFalse(func.called, - "press_only decorator doesn't redirect when request.journal is found") + self.assertFalse( + func.called, + "press_only decorator doesn't redirect when request.journal is found", + ) def test_submission_authorised_with_bad_user(self): func = Mock() @@ -3446,10 +3988,10 @@ def test_submission_authorised_with_bad_user(self): # enable submission authorisation setting setting_handler.save_setting( - setting_group_name='general', - setting_name='limit_access_to_submission', + setting_group_name="general", + setting_name="limit_access_to_submission", journal=self.journal_one, - value='On', + value="On", ) request = self.prepare_request_with_user( @@ -3472,10 +4014,10 @@ def test_submission_authorised_with_good_user(self): # enable submission authorisation setting setting_handler.save_setting( - setting_group_name='general', - setting_name='limit_access_to_submission', + setting_group_name="general", + setting_name="limit_access_to_submission", journal=self.journal_one, - value='On', + value="On", ) request = self.prepare_request_with_user( @@ -3497,10 +4039,10 @@ def test_submission_authorised_with_setting_off(self): # force disable submission authorisation setting setting_handler.save_setting( - setting_group_name='general', - setting_name='limit_access_to_submission', + setting_group_name="general", + setting_name="limit_access_to_submission", journal=self.journal_one, - value='', + value="", ) # user without roles to test that its not blocked @@ -3548,7 +4090,7 @@ def test_submission_authorised_with_good_user_repo(self): self.repository.save() role = core_models.Role.objects.get( - slug='author', + slug="author", ) repo_role = repository_models.RepositoryRole.objects.create( user=self.user_with_no_roles, @@ -3640,10 +4182,10 @@ def test_submission_authorised_journal_editor(self): decorated_func = decorators.submission_authorised(func) setting_handler.save_setting( - setting_group_name='general', - setting_name='limit_access_to_submission', + setting_group_name="general", + setting_name="limit_access_to_submission", journal=self.journal_one, - value='', + value="", ) # user user without roles to test that its not blocked @@ -3663,7 +4205,7 @@ def test_submission_authorised_journal_editor(self): @override_settings(URL_CONFIG="domain") def test_get_author_role(self): role = core_models.Role.objects.get( - slug='author', + slug="author", ) # test the access request form @@ -3672,18 +4214,18 @@ def test_get_author_role(self): user=self.user_with_no_roles, role=role, data={ - 'text': 'Here is my access request.', - } + "text": "Here is my access request.", + }, ) access_request = form.save() # test view for approving access data = { - 'approve': access_request.pk, + "approve": access_request.pk, } self.client.force_login(self.staff_member) self.client.post( - reverse('manage_access_requests'), + reverse("manage_access_requests"), data=data, SERVER_NAME="journal1.localhost", ) @@ -3696,7 +4238,7 @@ def test_get_author_role(self): ) self.assertTrue( account_role.exists(), - 'No account role was created during the post action on the manage_access_requests view', + "No account role was created during the post action on the manage_access_requests view", ) # delete the account role once we are done with it @@ -3705,9 +4247,7 @@ def test_get_author_role(self): def test_article_is_not_submitted_complete(self): func = Mock() decorated_func = decorators.article_is_not_submitted(func) - kwargs = { - 'article_id': self.article_unassigned.pk - } + kwargs = {"article_id": self.article_unassigned.pk} request = self.prepare_request_with_user( self.regular_user, @@ -3719,9 +4259,7 @@ def test_article_is_not_submitted_complete(self): def test_article_is_not_submitted_unsubmitted(self): func = Mock() decorated_func = decorators.article_is_not_submitted(func) - kwargs = { - 'article_id': self.article_unsubmitted.pk - } + kwargs = {"article_id": self.article_unsubmitted.pk} request = self.prepare_request_with_user( self.regular_user, @@ -3739,7 +4277,7 @@ def test_article_is_not_submitted_unsubmitted(self): def test_journal_manager_can_access_manager(self): self.client.force_login(self.journal_manager) response = self.client.get( - reverse('core_manager_index'), + reverse("core_manager_index"), SERVER_NAME="journal1.localhost", ) self.assertTrue( @@ -3750,7 +4288,7 @@ def test_journal_manager_can_access_manager(self): def test_journal_manager_can_access_licenses(self): self.client.force_login(self.journal_manager) response = self.client.get( - reverse('submission_licenses'), + reverse("submission_licenses"), SERVER_NAME="journal1.localhost", ) self.assertTrue( @@ -3761,44 +4299,44 @@ def test_journal_manager_can_access_licenses(self): def test_editor_can_edit_licenses(self): self.client.force_login(self.editor) response = self.client.get( - reverse('submission_licenses'), - SERVER_NAME='journal1.localhost', + reverse("submission_licenses"), + SERVER_NAME="journal1.localhost", ) self.assertTrue( response.status_code, 200, ) data = { - 'save': True, - 'name': 'Test License', - 'short_name': 'TEST', - 'url': 'https://janeway.systems', - 'text': 'This is a test license.', - 'order': 1, - 'available_for_submission': True, + "save": True, + "name": "Test License", + "short_name": "TEST", + "url": "https://janeway.systems", + "text": "This is a test license.", + "order": 1, + "available_for_submission": True, } self.client.post( - reverse('submission_licenses'), + reverse("submission_licenses"), data=data, - SERVER_NAME='journal1.localhost', + SERVER_NAME="journal1.localhost", ) self.assertTrue( submission_models.Licence.objects.filter( - short_name='TEST', + short_name="TEST", ).exists() ) def test_blocking_editor_from_licenses(self): # exclude the editor from accessing the licenses page. setting_handler.save_setting( - setting_group_name='permission', - setting_name='licenses', + setting_group_name="permission", + setting_name="licenses", journal=self.journal_one, value='["journal-manager"]', ) self.client.force_login(self.editor) response = self.client.get( - reverse('submission_licenses'), + reverse("submission_licenses"), SERVER_NAME="journal1.localhost", ) self.assertTrue( @@ -3808,8 +4346,8 @@ def test_blocking_editor_from_licenses(self): # reset the setting change, so it does not affect other tests setting_handler.save_setting( - setting_group_name='permission', - setting_name='licenses', + setting_group_name="permission", + setting_name="licenses", journal=self.journal_one, value='["editor", "journal-manager"]', ) @@ -3818,11 +4356,11 @@ def test_editor_can_access_edit_setting(self): self.client.force_login(self.editor) response = self.client.get( reverse( - 'core_edit_setting', + "core_edit_setting", kwargs={ - 'setting_group': 'general', - 'setting_name': 'journal_name', - } + "setting_group": "general", + "setting_name": "journal_name", + }, ), SERVER_NAME="journal1.localhost", ) @@ -3834,11 +4372,11 @@ def test_editor_can_access_edit_setting(self): def test_blocking_editor_from_editing_setting(self): # set the journal_name setting to only be editable by journal managers. journal_manager_role = core_models.Role.objects.get( - slug='journal-manager', + slug="journal-manager", ) setting = core_models.Setting.objects.get( - group__name='general', - name='journal_name', + group__name="general", + name="journal_name", ) setting.editable_by.clear() setting.editable_by.add(journal_manager_role) @@ -3846,11 +4384,11 @@ def test_blocking_editor_from_editing_setting(self): self.client.force_login(self.editor) response = self.client.get( reverse( - 'core_edit_setting', + "core_edit_setting", kwargs={ - 'setting_group': 'general', - 'setting_name': 'journal_name', - } + "setting_group": "general", + "setting_name": "journal_name", + }, ), SERVER_NAME="journal1.localhost", ) @@ -3861,7 +4399,7 @@ def test_blocking_editor_from_editing_setting(self): # add editor back to reset this change. editor_role = core_models.Role.objects.get( - slug='editor', + slug="editor", ) setting.editable_by.add(editor_role) @@ -3869,26 +4407,23 @@ def test_setting_is_available_in_group(self): self.client.force_login(self.editor) response = self.client.get( reverse( - 'core_edit_settings_group', + "core_edit_settings_group", kwargs={ - 'display_group': 'journal', - } + "display_group": "journal", + }, ), SERVER_NAME="journal1.localhost", ) - self.assertContains( - response, - 'Journal Name' - ) + self.assertContains(response, "Journal Name") def test_setting_is_removed_from_group(self): # set the journal_name setting to only be editable by journal managers. journal_manager_role = core_models.Role.objects.get( - slug='journal-manager', + slug="journal-manager", ) setting = core_models.Setting.objects.get( - group__name='general', - name='journal_name', + group__name="general", + name="journal_name", ) setting.editable_by.clear() setting.editable_by.add(journal_manager_role) @@ -3896,55 +4431,46 @@ def test_setting_is_removed_from_group(self): self.client.force_login(self.editor) response = self.client.get( reverse( - 'core_edit_settings_group', + "core_edit_settings_group", kwargs={ - 'display_group': 'journal', - } + "display_group": "journal", + }, ), SERVER_NAME="journal1.localhost", ) - self.assertNotContains( - response, - 'Journal Name' - ) + self.assertNotContains(response, "Journal Name") # add editor back to reset this change. editor_role = core_models.Role.objects.get( - slug='editor', + slug="editor", ) setting.editable_by.add(editor_role) def test_setting_is_enabled_permission_denied(self): func = Mock() decorator = decorators.setting_is_enabled( - setting_name='enable_share_reviews_decision', - setting_group_name='general', + setting_name="enable_share_reviews_decision", + setting_group_name="general", ) decorated_func = decorator(func) - request = self.prepare_request_with_user( - self.editor, - self.journal_one - ) + request = self.prepare_request_with_user(self.editor, self.journal_one) with self.assertRaises(PermissionDenied): decorated_func(request) def test_setting_is_enabled_access_granted(self): setting_handler.save_setting( - setting_group_name='general', - setting_name='enable_share_reviews_decision', + setting_group_name="general", + setting_name="enable_share_reviews_decision", journal=self.journal_one, - value='On', + value="On", ) func = Mock() decorator = decorators.setting_is_enabled( - setting_name='enable_share_reviews_decision', - setting_group_name='general', + setting_name="enable_share_reviews_decision", + setting_group_name="general", ) decorated_func = decorator(func) - request = self.prepare_request_with_user( - self.editor, - self.journal_one - ) + request = self.prepare_request_with_user(self.editor, self.journal_one) decorated_func(request) self.assertTrue( func.called, @@ -3954,9 +4480,7 @@ def test_setting_is_enabled_access_granted(self): def test_user_has_completed_review_for_article_denied(self): func = Mock() decorated_func = decorators.user_has_completed_review_for_article(func) - kwargs = { - 'article_id': self.article_review_completed.pk - } + kwargs = {"article_id": self.article_review_completed.pk} request = self.prepare_request_with_user( self.second_user, @@ -3970,9 +4494,7 @@ def test_user_has_completed_review_for_article_denied(self): def test_user_has_completed_review_for_article_granted(self): func = Mock() decorated_func = decorators.user_has_completed_review_for_article(func) - kwargs = { - 'article_id': self.article_review_completed.pk - } + kwargs = {"article_id": self.article_review_completed.pk} request = self.prepare_request_with_user( self.regular_user, journal=self.journal_one, @@ -3991,25 +4513,28 @@ def test_section_editor_cannot_see_pii_when_enabled(self): the response. """ with context_managers.janeway_setting_override( - "permission", "se_pii_filter", self.article_in_review.journal, True, + "permission", + "se_pii_filter", + self.article_in_review.journal, + True, ): self.client.force_login(self.section_editor) article_views = [ - 'manage_article_log', - 'edit_metadata', - 'review_unassigned_article', - 'review_in_review', - 'review_decision', - 'decision_helper', - 'review_request_revisions', - 'request_revisions_notification', - 'review_view_review' + "manage_article_log", + "edit_metadata", + "review_unassigned_article", + "review_in_review", + "review_decision", + "decision_helper", + "review_request_revisions", + "request_revisions_notification", + "review_view_review", ] general_views = [ - 'core_dashboard', - 'review_home', - 'core_active_submissions', - 'review_unassigned', + "core_dashboard", + "review_home", + "core_active_submissions", + "review_unassigned", ] list_of_pii_strings = self.get_pii_strings_for_article( self.article_in_review, @@ -4023,21 +4548,21 @@ def test_section_editor_cannot_see_pii_when_enabled(self): SERVER_NAME=self.article_in_review.journal.domain, ) found_strings = [ - string in response.content.decode('utf-8') for string in - list_of_pii_strings + string in response.content.decode("utf-8") + for string in list_of_pii_strings ] self.assertFalse(any(found_strings)) for view_name in article_views: kwargs = { - 'article_id': self.article_in_review.pk, + "article_id": self.article_in_review.pk, } - if view_name == 'review_decision': - kwargs['decision'] = 'accept' - elif view_name == 'request_revisions_notification': - kwargs['revision_id'] = self.air_revision_request.pk - elif view_name == 'review_view_review': - kwargs['review_id'] = self.air_completed_review.pk + if view_name == "review_decision": + kwargs["decision"] = "accept" + elif view_name == "request_revisions_notification": + kwargs["revision_id"] = self.air_revision_request.pk + elif view_name == "review_view_review": + kwargs["review_id"] = self.air_completed_review.pk response = self.client.get( reverse( view_name, @@ -4046,8 +4571,8 @@ def test_section_editor_cannot_see_pii_when_enabled(self): SERVER_NAME=self.article_in_review.journal.domain, ) found_strings = [ - string in response.content.decode('utf-8') for string in - list_of_pii_strings + string in response.content.decode("utf-8") + for string in list_of_pii_strings ] self.assertFalse(any(found_strings)) @@ -4055,16 +4580,16 @@ def test_section_editor_cannot_see_pii_when_enabled(self): @staticmethod def create_user( - username, - roles=None, - journal=None, - first_name='', - last_name='', - institution='', - department='', - orcid='', - country_name='', - country_code='', + username, + roles=None, + journal=None, + first_name="", + last_name="", + institution="", + department="", + orcid="", + country_name="", + country_code="", ): """ Creates a user with the specified permissions. @@ -4083,13 +4608,13 @@ def create_user( roles=roles, journal=journal, **{ - 'first_name': first_name, - 'last_name': last_name, - 'institution': institution, - 'department': department, - 'orcid': orcid, - 'country': country_obj, - } + "first_name": first_name, + "last_name": last_name, + "institution": institution, + "department": department, + "orcid": orcid, + "country": country_obj, + }, ) @staticmethod @@ -4124,7 +4649,7 @@ def get_pii_strings_for_article(article): pii_strings.append(fa.first_name) pii_strings.append(fa.last_name) pii_strings.append(fa.email) - pii_strings.append(fa.orcid if fa.orcid else '') + pii_strings.append(fa.orcid if fa.orcid else "") pii_strings.append(fa.institution) pii_strings.append(fa.department) pii_strings.append(fa.country) @@ -4133,7 +4658,9 @@ def get_pii_strings_for_article(article): pii_strings.append(article.correspondence_author.email) pii_strings.append(article.correspondence_author.department) pii_strings.append( - article.correspondence_author.orcid if article.correspondence_author.orcid else '' + article.correspondence_author.orcid + if article.correspondence_author.orcid + else "" ) pii_strings.append(article.correspondence_author.institution) pii_strings.append(article.correspondence_author.country) @@ -4146,24 +4673,36 @@ def setUpTestData(self): :return: None """ self.journal_one, self.journal_two = self.create_journals() - self.create_roles(["editor", "author", "reviewer", "proofreader", - "production", "copyeditor", "typesetter", - "proofing-manager", "section-editor"]) + self.create_roles( + [ + "editor", + "author", + "reviewer", + "proofreader", + "production", + "copyeditor", + "typesetter", + "proofing-manager", + "section-editor", + ] + ) self.regular_user = self.create_user( "redshirt@voyager.com", - first_name='Tim', - last_name='Redshirt', - institution='Starfleet Ops', - department='Canon Fodder', - orcid='1234-1234-1234-0000', - country_name='United States of America', - country_code='US', + first_name="Tim", + last_name="Redshirt", + institution="Starfleet Ops", + department="Canon Fodder", + orcid="1234-1234-1234-0000", + country_name="United States of America", + country_code="US", ) self.regular_user.is_active = True self.regular_user.save() - self.second_user = self.create_user("seconduser@martineve.com", ["reviewer"], journal=self.journal_one) + self.second_user = self.create_user( + "seconduser@martineve.com", ["reviewer"], journal=self.journal_one + ) self.second_user.is_active = True self.second_user.save() @@ -4172,12 +4711,17 @@ def setUpTestData(self): self.admin_user.is_active = True self.admin_user.save() - self.inactive_user = self.create_user("disableduser@martineve.com", ["editor", "author", "proofreader", - "production"], journal=self.journal_one) + self.inactive_user = self.create_user( + "disableduser@martineve.com", + ["editor", "author", "proofreader", "production"], + journal=self.journal_one, + ) self.inactive_user.is_active = False self.inactive_user.save() - self.editor = self.create_user("editoruser@martineve.com", ["editor"], journal=self.journal_one) + self.editor = self.create_user( + "editoruser@martineve.com", ["editor"], journal=self.journal_one + ) self.editor.is_active = True self.editor.save() @@ -4187,44 +4731,55 @@ def setUpTestData(self): journal=self.journal_one, first_name="Belanna", last_name="Torres", - institution='Starfleet', - department='Engineering', - orcid='0000-1234-1234-1234', - country_code='FR', - country_name='France', + institution="Starfleet", + department="Engineering", + orcid="0000-1234-1234-1234", + country_code="FR", + country_name="France", ) self.author.is_active = True self.author.save() - self.proofreader = self.create_user("proofreader@martineve.com", ["proofreader"], journal=self.journal_one) + self.proofreader = self.create_user( + "proofreader@martineve.com", ["proofreader"], journal=self.journal_one + ) self.proofreader.is_active = True self.proofreader.save() - self.proofreader_two = self.create_user("proofreader2@martineve.com", ["proofreader"], journal=self.journal_one) + self.proofreader_two = self.create_user( + "proofreader2@martineve.com", ["proofreader"], journal=self.journal_one + ) self.proofreader_two.is_active = True self.proofreader_two.save() - self.production = self.create_user("production@martineve.com", ["production"], journal=self.journal_one) + self.production = self.create_user( + "production@martineve.com", ["production"], journal=self.journal_one + ) self.production.is_active = True self.production.save() - self.copyeditor = self.create_user("copyeditor@martineve.com", ["copyeditor"], journal=self.journal_one) + self.copyeditor = self.create_user( + "copyeditor@martineve.com", ["copyeditor"], journal=self.journal_one + ) self.copyeditor.is_active = True self.copyeditor.save() - self.typesetter = self.create_user("typesetter@martineve.com", ["typesetter"], journal=self.journal_one) + self.typesetter = self.create_user( + "typesetter@martineve.com", ["typesetter"], journal=self.journal_one + ) self.typesetter.is_active = True self.typesetter.save() - self.other_typesetter = self.create_user("other_typesetter@martineve.com", ["typesetter"], - journal=self.journal_one) + self.other_typesetter = self.create_user( + "other_typesetter@martineve.com", ["typesetter"], journal=self.journal_one + ) self.other_typesetter.is_active = True self.other_typesetter.save() self.proofing_manager = self.create_user( "proofing_manager@martineve.com", ["proofing-manager"], - journal=self.journal_one + journal=self.journal_one, ) self.proofing_manager.is_active = True self.proofing_manager.save() @@ -4232,82 +4787,95 @@ def setUpTestData(self): self.other_typesetter.is_active = True self.other_typesetter.save() - self.section_editor = self.create_user("section_editor@martineve.com", ['section-editor'], - journal=self.journal_one) + self.section_editor = self.create_user( + "section_editor@martineve.com", ["section-editor"], journal=self.journal_one + ) self.section_editor.is_active = True self.section_editor.save() self.second_section_editor = self.create_user( "second_section_editor@martineve.com", - ['section-editor'], + ["section-editor"], journal=self.journal_one, ) self.second_section_editor.is_active = True self.second_section_editor.save() - self.second_reviewer = self.create_user("second_reviewer@martineve.com", ['reviewer'], - journal=self.journal_one) + self.second_reviewer = self.create_user( + "second_reviewer@martineve.com", ["reviewer"], journal=self.journal_one + ) self.second_reviewer.is_active = True self.second_reviewer.save() - self.user_with_no_roles = self.create_user("no_roles@janeway.systems", [], journal=self.journal_one) + self.user_with_no_roles = self.create_user( + "no_roles@janeway.systems", [], journal=self.journal_one + ) self.user_with_no_roles.is_active = True self.user_with_no_roles.save() - self.staff_member = self.create_user("staff@janeway.systems", [], journal=self.journal_one) + self.staff_member = self.create_user( + "staff@janeway.systems", [], journal=self.journal_one + ) self.staff_member.is_active = True self.staff_member.is_staff = True self.staff_member.save() self.repo_manager = self.create_user( "repomanager@janeway.systems", - first_name='Tom', - last_name='Paris', + first_name="Tom", + last_name="Paris", ) self.repo_manager.is_active = True self.repo_manager.save() self.journal_manager = self.create_user( "journalmanager@janeway.systems", - ['journal-manager'], + ["journal-manager"], journal=self.journal_one, ) - self.public_file = core_models.File(mime_type="A/FILE", - original_filename="blah.txt", - uuid_filename="UUID.txt", - label="A file that is public", - description="Oh yes, it's a file", - owner=self.regular_user, - is_galley=False, - privacy="public") + self.public_file = core_models.File( + mime_type="A/FILE", + original_filename="blah.txt", + uuid_filename="UUID.txt", + label="A file that is public", + description="Oh yes, it's a file", + owner=self.regular_user, + is_galley=False, + privacy="public", + ) self.public_file.save() - self.private_file = core_models.File(mime_type="A/FILE", - original_filename="blah.txt", - uuid_filename="UUID.txt", - label="A file that is private", - description="Oh yes, it's a file", - owner=self.regular_user, - is_galley=False, - privacy="owner") + self.private_file = core_models.File( + mime_type="A/FILE", + original_filename="blah.txt", + uuid_filename="UUID.txt", + label="A file that is private", + description="Oh yes, it's a file", + owner=self.regular_user, + is_galley=False, + privacy="owner", + ) self.private_file.save() - self.third_file = core_models.File(mime_type="A/FILE", - original_filename="blah.txt", - uuid_filename="UUID.txt", - label="A file that is private", - description="Oh yes, it's a file", - owner=self.author, - is_galley=False, - privacy="owner") + self.third_file = core_models.File( + mime_type="A/FILE", + original_filename="blah.txt", + uuid_filename="UUID.txt", + label="A file that is private", + description="Oh yes, it's a file", + owner=self.author, + is_galley=False, + privacy="owner", + ) self.third_file.save() self.article_in_review = submission_models.Article.objects.create( - owner=self.regular_user, title="A Test Article in review", + owner=self.regular_user, + title="A Test Article in review", abstract="An abstract", stage=submission_models.STAGE_UNDER_REVIEW, journal_id=self.journal_one.id, @@ -4324,13 +4892,13 @@ def setUpTestData(self): review_models.EditorAssignment.objects.get_or_create( article=self.article_in_review, editor=self.section_editor, - editor_type='section-editor', + editor_type="section-editor", notified=True, ) self.air_revision_request = review_models.RevisionRequest.objects.create( article=self.article_in_review, editor=self.section_editor, - editor_note='Hey, this is just a test! No sweat.', + editor_note="Hey, this is just a test! No sweat.", date_due=timezone.now(), ) self.air_round = review_models.ReviewRound.objects.create( @@ -4346,68 +4914,92 @@ def setUpTestData(self): date_complete=timezone.now(), is_complete=True, ) - self.article_in_production = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_TYPESETTING, - journal_id=self.journal_one.id) + self.article_in_production = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_TYPESETTING, + journal_id=self.journal_one.id, + ) self.article_in_production.save() self.article_in_production.data_figure_files.add(self.public_file) - self.article_in_proofing = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_PROOFING, - journal_id=self.journal_one.id) + self.article_in_proofing = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_PROOFING, + journal_id=self.journal_one.id, + ) self.article_in_proofing.save() self.article_in_proofing.data_figure_files.add(self.public_file) - self.proofing_assigned = production_models.ProductionAssignment(article=self.article_in_proofing, - production_manager=self.production) + self.proofing_assigned = production_models.ProductionAssignment( + article=self.article_in_proofing, production_manager=self.production + ) self.proofing_assigned.save() - self.article_unsubmitted = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNSUBMITTED, - journal_id=self.journal_one.id) + self.article_unsubmitted = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNSUBMITTED, + journal_id=self.journal_one.id, + ) self.article_unsubmitted.save() - self.article_unassigned = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNASSIGNED, - journal_id=self.journal_one.id, - date_submitted=timezone.now()) + self.article_unassigned = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNASSIGNED, + journal_id=self.journal_one.id, + date_submitted=timezone.now(), + ) self.article_unassigned.save() - self.article_assigned = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ASSIGNED, - journal_id=self.journal_one.id) + self.article_assigned = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ASSIGNED, + journal_id=self.journal_one.id, + ) self.article_assigned.save() - self.article_under_review = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNDER_REVIEW, - journal_id=self.journal_one.id) + self.article_under_review = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNDER_REVIEW, + journal_id=self.journal_one.id, + ) self.article_under_review.save() - self.article_review_completed = submission_models.Article.objects.create(owner=self.regular_user, - title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id, - date_accepted=timezone.now()) + self.article_review_completed = submission_models.Article.objects.create( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + date_accepted=timezone.now(), + ) - self.article_author_is_owner = submission_models.Article.objects.create(owner=self.author, - title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id, - date_accepted=timezone.now()) + self.article_author_is_owner = submission_models.Article.objects.create( + owner=self.author, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + date_accepted=timezone.now(), + ) self.article_author_is_owner.authors.add(self.editor) self.article_author_is_owner.authors.add(self.author) - self.review_form = review_models.ReviewForm(name="A Form", intro="i", thanks="t", - journal=self.journal_one) + self.review_form = review_models.ReviewForm( + name="A Form", intro="i", thanks="t", journal=self.journal_one + ) self.review_form.save() self.review_assignment_complete = review_models.ReviewAssignment( @@ -4423,153 +5015,203 @@ def setUpTestData(self): self.review_assignment_complete.save() - self.review_assignment = review_models.ReviewAssignment(article=self.article_under_review, - reviewer=self.second_user, - editor=self.editor, - date_due=datetime.datetime.now(), - form=self.review_form) + self.review_assignment = review_models.ReviewAssignment( + article=self.article_under_review, + reviewer=self.second_user, + editor=self.editor, + date_due=datetime.datetime.now(), + form=self.review_form, + ) self.review_assignment.save() - self.review_assignment_not_in_scope = review_models.ReviewAssignment(article=self.article_in_production, - reviewer=self.regular_user, - editor=self.editor, - date_due=datetime.datetime.now(), - form=self.review_form) + self.review_assignment_not_in_scope = review_models.ReviewAssignment( + article=self.article_in_production, + reviewer=self.regular_user, + editor=self.editor, + date_due=datetime.datetime.now(), + form=self.review_form, + ) self.review_assignment_not_in_scope.save() - self.article_under_revision = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_UNDER_REVISION, - journal_id=self.journal_one.id) + self.article_under_revision = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_UNDER_REVISION, + journal_id=self.journal_one.id, + ) self.article_under_revision.save() - self.article_rejected = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_REJECTED, - journal_id=self.journal_one.id) + self.article_rejected = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_REJECTED, + journal_id=self.journal_one.id, + ) self.article_rejected.save() - self.article_accepted = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_ACCEPTED, - journal_id=self.journal_one.id) + self.article_accepted = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_ACCEPTED, + journal_id=self.journal_one.id, + ) self.article_accepted.save() - self.section_editor_assignment = review_models.EditorAssignment(article=self.article_assigned, - editor=self.section_editor, - editor_type='section-editor', - notified=True) + self.section_editor_assignment = review_models.EditorAssignment( + article=self.article_assigned, + editor=self.section_editor, + editor_type="section-editor", + notified=True, + ) self.section_editor_assignment.save() self.production_section_editor_assignment = review_models.EditorAssignment( article=self.article_in_production, editor=self.section_editor, - editor_type='section-editor', - notified=True) + editor_type="section-editor", + notified=True, + ) self.production_section_editor_assignment.save() - self.article_editor_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_EDITOR_COPYEDITING, - journal_id=self.journal_one.id) + self.article_editor_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_EDITOR_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_editor_copyediting.save() - self.article_author_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_AUTHOR_COPYEDITING, - journal_id=self.journal_one.id) + self.article_author_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_AUTHOR_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_author_copyediting.save() - self.article_final_copyediting = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_FINAL_COPYEDITING, - journal_id=self.journal_one.id) + self.article_final_copyediting = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_FINAL_COPYEDITING, + journal_id=self.journal_one.id, + ) self.article_final_copyediting.save() - self.article_proofing = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_PROOFING, - journal_id=self.journal_one.id) + self.article_proofing = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_PROOFING, + journal_id=self.journal_one.id, + ) self.article_proofing.save() self.test_galley = core_models.Galley( - article=self.article_proofing, - file=self.third_file, - label='TXT' + article=self.article_proofing, file=self.third_file, label="TXT" ) self.test_galley.save() - assigned = production_models.ProductionAssignment(article=self.article_in_production, - production_manager=self.production) + assigned = production_models.ProductionAssignment( + article=self.article_in_production, production_manager=self.production + ) assigned.save() - self.article_published = submission_models.Article(owner=self.regular_user, title="A Second Test Article", - abstract="An abstract", - stage=submission_models.STAGE_PUBLISHED, - journal_id=self.journal_one.id) + self.article_published = submission_models.Article( + owner=self.regular_user, + title="A Second Test Article", + abstract="An abstract", + stage=submission_models.STAGE_PUBLISHED, + journal_id=self.journal_one.id, + ) self.article_published.save() - assigned = production_models.ProductionAssignment(article=self.article_published, - production_manager=self.production) + assigned = production_models.ProductionAssignment( + article=self.article_published, production_manager=self.production + ) assigned.save() - self.article_in_production_inactive = submission_models.Article(owner=self.regular_user, title="A Test Article", - abstract="An abstract", - stage=submission_models.STAGE_TYPESETTING, - journal_id=self.journal_one.id) + self.article_in_production_inactive = submission_models.Article( + owner=self.regular_user, + title="A Test Article", + abstract="An abstract", + stage=submission_models.STAGE_TYPESETTING, + journal_id=self.journal_one.id, + ) self.article_in_production_inactive.save() - self.assigned = production_models.ProductionAssignment(article=self.article_in_production_inactive, - production_manager=self.inactive_user) + self.assigned = production_models.ProductionAssignment( + article=self.article_in_production_inactive, + production_manager=self.inactive_user, + ) self.assigned.save() - self.copyedit_assignment = copyediting_models.CopyeditAssignment(article=self.article_editor_copyediting, - editor=self.editor, - copyeditor=self.copyeditor, - due=timezone.now(), - assigned=timezone.now(), - notified=True, - decision='accepted', - date_decided=timezone.now()) + self.copyedit_assignment = copyediting_models.CopyeditAssignment( + article=self.article_editor_copyediting, + editor=self.editor, + copyeditor=self.copyeditor, + due=timezone.now(), + assigned=timezone.now(), + notified=True, + decision="accepted", + date_decided=timezone.now(), + ) self.copyedit_assignment.save() - self.typeset_task = production_models.TypesetTask(assignment=self.assigned, - typesetter=self.typesetter, - notified=True, - accepted=timezone.now()) + self.typeset_task = production_models.TypesetTask( + assignment=self.assigned, + typesetter=self.typesetter, + notified=True, + accepted=timezone.now(), + ) self.typeset_task.save() - self.other_typeset_task = production_models.TypesetTask(assignment=self.assigned, - typesetter=self.other_typesetter, - notified=True, - accepted=timezone.now()) + self.other_typeset_task = production_models.TypesetTask( + assignment=self.assigned, + typesetter=self.other_typesetter, + notified=True, + accepted=timezone.now(), + ) self.other_typeset_task.save() - self.proofing_assignment = proofing_models.ProofingAssignment(article=self.article_proofing, - proofing_manager=self.proofing_manager, - notified=True) + self.proofing_assignment = proofing_models.ProofingAssignment( + article=self.article_proofing, + proofing_manager=self.proofing_manager, + notified=True, + ) self.proofing_assignment.save() self.proofing_assignment.add_new_proofing_round() - self.proofing_task = proofing_models.ProofingTask(round=self.proofing_assignment.current_proofing_round(), - proofreader=self.proofreader, - notified=True, - due=timezone.now(), - accepted=timezone.now(), - task='sdfsdffs') + self.proofing_task = proofing_models.ProofingTask( + round=self.proofing_assignment.current_proofing_round(), + proofreader=self.proofreader, + notified=True, + due=timezone.now(), + accepted=timezone.now(), + task="sdfsdffs", + ) self.proofing_task.save() self.proofing_task.galleys_for_proofing.add(self.test_galley) - self.correction_task = proofing_models.TypesetterProofingTask(proofing_task=self.proofing_task, - typesetter=self.typesetter, - notified=True, - due=timezone.now(), - accepted=timezone.now(), - task='fsddsff') + self.correction_task = proofing_models.TypesetterProofingTask( + proofing_task=self.proofing_task, + typesetter=self.typesetter, + notified=True, + due=timezone.now(), + accepted=timezone.now(), + task="fsddsff", + ) self.correction_task.save() - self.press = press_models.Press.objects.create(name='CTP Press', domain='testserver') + self.press = press_models.Press.objects.create( + name="CTP Press", domain="testserver" + ) self.repository, self.repository_subject = helpers.create_repository( self.press, @@ -4582,8 +5224,8 @@ def setUpTestData(self): subject=self.repository_subject, ) - call_command('load_default_settings') - call_command('load_permissions') + call_command("load_default_settings") + call_command("load_permissions") @staticmethod def mock_messages_add(level, message, extra_tags): @@ -4609,8 +5251,8 @@ def prepare_request_with_user(user, journal=None, press=None, repository=None): request.journal = journal request._messages = Mock() request._messages.add = TestSecurity.mock_messages_add - request.path = '/a/fake/path/' - request.path_info = '/a/fake/path/' + request.path = "/a/fake/path/" + request.path_info = "/a/fake/path/" request.press = press request.repository = repository diff --git a/src/submission/admin.py b/src/submission/admin.py index 239b05f5a3..f4b224bb1e 100755 --- a/src/submission/admin.py +++ b/src/submission/admin.py @@ -17,69 +17,91 @@ def label_from_instance(self, obj): class ArticleFundingAdmin(admin.ModelAdmin): - list_display = ('name', 'article', 'fundref_id', 'funding_id') - list_filter = ('article__journal',) - search_fields = ('name', 'fundref_id', 'funding_id') - raw_id_fields = ('article',) + list_display = ("name", "article", "fundref_id", "funding_id") + list_filter = ("article__journal",) + search_fields = ("name", "fundref_id", "funding_id") + raw_id_fields = ("article",) class FrozenAuthorAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('pk', 'first_name', 'last_name', - 'frozen_email', 'frozen_orcid', 'institution', '_journal') - list_filter = ('article__journal',) - search_fields = ('frozen_email', 'frozen_orcid', - 'first_name', 'last_name', - 'institution', 'frozen_biography', ) - raw_id_fields = ('article', 'author',) + list_display = ( + "pk", + "first_name", + "last_name", + "frozen_email", + "frozen_orcid", + "institution", + "_journal", + ) + list_filter = ("article__journal",) + search_fields = ( + "frozen_email", + "frozen_orcid", + "first_name", + "last_name", + "institution", + "frozen_biography", + ) + raw_id_fields = ( + "article", + "author", + ) class ArticleAdmin(admin_utils.JanewayModelAdmin): - list_display = ('pk', 'title', 'correspondence_author', - 'journal', 'date_submitted', 'stage', - 'owner', 'is_import') + list_display = ( + "pk", + "title", + "correspondence_author", + "journal", + "date_submitted", + "stage", + "owner", + "is_import", + ) search_fields = ( - 'pk', - 'title', - 'correspondence_author__email', - 'correspondence_author__first_name', - 'correspondence_author__last_name', - 'owner__email', - 'owner__first_name', - 'owner__last_name', + "pk", + "title", + "correspondence_author__email", + "correspondence_author__first_name", + "correspondence_author__last_name", + "owner__email", + "owner__first_name", + "owner__last_name", ) list_filter = ( - 'journal', - 'stage', - 'is_import', - 'peer_reviewed', - 'date_submitted', - 'date_accepted', - 'date_declined', - 'date_published', - 'date_updated', - 'last_modified', + "journal", + "stage", + "is_import", + "peer_reviewed", + "date_submitted", + "date_accepted", + "date_declined", + "date_published", + "date_updated", + "last_modified", ) raw_id_fields = ( - 'section', - 'owner', - 'license', - 'authors', - 'correspondence_author', - 'primary_issue', - 'projected_issue', - 'render_galley', - 'large_image_file', - 'thumbnail_image_file', - 'preprint_journal_article', - 'source_files', - 'manuscript_files', - 'data_figure_files', - 'supplementary_files', - 'publisher_notes', + "section", + "owner", + "license", + "authors", + "correspondence_author", + "primary_issue", + "projected_issue", + "render_galley", + "large_image_file", + "thumbnail_image_file", + "preprint_journal_article", + "source_files", + "manuscript_files", + "data_figure_files", + "supplementary_files", + "publisher_notes", ) filter_horizontal = ( - 'authors', - 'keywords', + "authors", + "keywords", ) inlines = [ @@ -98,60 +120,70 @@ def get_queryset(self, request): class ArticleLogAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_article', '_journal', 'stage_from', - 'stage_to', 'date_time') - list_filter = ('article__journal', 'stage_from', 'stage_to') - search_fields = ('article__pk', 'article__title', - 'stage_from', 'stage_to') - date_hierarchy = ('date_time') - readonly_fields = ('date_time',) + list_display = ("_article", "_journal", "stage_from", "stage_to", "date_time") + list_filter = ("article__journal", "stage_from", "stage_to") + search_fields = ("article__pk", "article__title", "stage_from", "stage_to") + date_hierarchy = "date_time" + readonly_fields = ("date_time",) def _article(self, obj): - return truncatewords_html(str(obj.article), 10) if obj else '' + return truncatewords_html(str(obj.article), 10) if obj else "" class LicenseAdmin(admin.ModelAdmin): - list_display = ('name', 'short_name', 'journal', 'url', '_text') - list_filter = ('journal', 'short_name', 'url') - search_fields = ('name', 'short_name', 'url', 'text') + list_display = ("name", "short_name", "journal", "url", "_text") + list_filter = ("journal", "short_name", "url") + search_fields = ("name", "short_name", "url", "text") def _text(self, obj): - return truncatewords_html(obj.text, 8) if obj else '' + return truncatewords_html(obj.text, 8) if obj else "" class NoteAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('_text', '_article', '_journal', 'creator', 'date_time') - list_filter = ('article__journal', 'date_time',) - raw_id_fields = ('article', 'creator') - date_hierarchy = ('date_time') - search_fields = ('text', 'article__pk', 'article__title', - 'creator__email', 'creator__first_name', - 'creator__last_name') - raw_id_fields = ('creator',) + list_display = ("_text", "_article", "_journal", "creator", "date_time") + list_filter = ( + "article__journal", + "date_time", + ) + raw_id_fields = ("article", "creator") + date_hierarchy = "date_time" + search_fields = ( + "text", + "article__pk", + "article__title", + "creator__email", + "creator__first_name", + "creator__last_name", + ) + raw_id_fields = ("creator",) def _text(self, obj): - return truncatewords_html(obj.text, 10) if obj else '' + return truncatewords_html(obj.text, 10) if obj else "" def _article(self, obj): - return truncatewords_html(str(obj.article), 10) if obj else '' + return truncatewords_html(str(obj.article), 10) if obj else "" class PublisherNoteAdmin(admin.ModelAdmin): - list_display = ('_text', 'creator', 'date_time', 'sequence') - list_filter = ('date_time',) - date_hierarchy = ('date_time') - search_fields = ('text', 'creator__email', 'creator__first_name', - 'creator__last_name') - raw_id_fields = ('creator',) + list_display = ("_text", "creator", "date_time", "sequence") + list_filter = ("date_time",) + date_hierarchy = "date_time" + search_fields = ( + "text", + "creator__email", + "creator__first_name", + "creator__last_name", + ) + raw_id_fields = ("creator",) def _text(self, obj): - return truncatewords_html(obj.text, 10) if obj else '' + return truncatewords_html(obj.text, 10) if obj else "" class KeywordAdmin(admin.ModelAdmin): - list_display = ('word',) - list_filter = ('keywordarticle__article__journal',) - search_fields = ('word',) + list_display = ("word",) + list_filter = ("keywordarticle__article__journal",) + search_fields = ("word",) inlines = [ admin_utils.KeywordArticleInline, @@ -159,54 +191,71 @@ class KeywordAdmin(admin.ModelAdmin): class SectionAdmin(admin.ModelAdmin): - list_display = ('name', 'journal', 'article_count', - 'number_of_reviewers', 'is_filterable', - 'public_submissions', 'indexing') - list_filter = ('journal', 'is_filterable', 'public_submissions', - 'indexing') - search_fields = ('name',) - raw_id_fields = ('editors', 'section_editors') + list_display = ( + "name", + "journal", + "article_count", + "number_of_reviewers", + "is_filterable", + "public_submissions", + "indexing", + ) + list_filter = ("journal", "is_filterable", "public_submissions", "indexing") + search_fields = ("name",) + raw_id_fields = ("editors", "section_editors") @staticmethod def apply_select_related(self, qs): - return qs.prefetch_related('journal') + return qs.prefetch_related("journal") class FieldAdmin(admin.ModelAdmin): - list_display = ('name', 'journal', 'press', 'kind', - 'width', 'required', 'display') - list_filter = ('journal', 'press', 'kind', 'width', - 'required', 'display') - search_fields = ('name', 'help_text', 'choices') + list_display = ("name", "journal", "press", "kind", "width", "required", "display") + list_filter = ("journal", "press", "kind", "width", "required", "display") + search_fields = ("name", "help_text", "choices") class FieldAnswerAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('field', '_answer', '_article', '_journal') - list_filter = ('article__journal',) - search_fields = ('field__name', 'article__pk', 'article__title', - 'answer') + list_display = ("field", "_answer", "_article", "_journal") + list_filter = ("article__journal",) + search_fields = ("field__name", "article__pk", "article__title", "answer") def _answer(self, obj): - return truncatewords_html(obj.answer, 10) if obj else '' + return truncatewords_html(obj.answer, 10) if obj else "" class SubmissionConfigAdmin(admin.ModelAdmin): - list_display = ('pk', 'journal', - 'copyright_notice', 'competing_interests', - 'comments_to_the_editor', 'abstract', - 'language', 'license', 'keywords', 'section', - 'figures_data', 'default_license', - 'default_language', 'default_section', - 'submission_file_text') - raw_id_fields = ('default_license', 'default_section') + list_display = ( + "pk", + "journal", + "copyright_notice", + "competing_interests", + "comments_to_the_editor", + "abstract", + "language", + "license", + "keywords", + "section", + "figures_data", + "default_license", + "default_language", + "default_section", + "submission_file_text", + ) + raw_id_fields = ("default_license", "default_section") class ArticleAuthorOrderAdmin(admin_utils.ArticleFKModelAdmin): - list_display = ('order', '_article', 'author', '_journal') - list_filter = ('article__journal', 'order') - search_fields = ('article__pk', 'article__title', 'author__email', - 'author__first_name', 'author__last_name') - raw_id_fields = ('article', 'author') + list_display = ("order", "_article", "author", "_journal") + list_filter = ("article__journal", "order") + search_fields = ( + "article__pk", + "article__title", + "author__email", + "author__first_name", + "author__last_name", + ) + raw_id_fields = ("article", "author") admin_list = [ diff --git a/src/submission/decorators.py b/src/submission/decorators.py index 3b4197fbf4..7f8e05975b 100755 --- a/src/submission/decorators.py +++ b/src/submission/decorators.py @@ -8,7 +8,7 @@ def submission_is_enabled(func): - """ This decorator checks that a user is a reviewer, Note that this decorator does NOT check for conflict of + """This decorator checks that a user is a reviewer, Note that this decorator does NOT check for conflict of interest problems. Use the article_editor_user_required decorator (not yet written) to do a check against an article. @@ -19,15 +19,18 @@ def submission_is_enabled(func): def submission_is_enabled_wrapper(request, *args, **kwargs): if not request.journal: raise PermissionDenied( - _('This page can only be accessed on Journals.'), + _("This page can only be accessed on Journals."), ) - if request.journal.get_setting( - 'general', - 'disable_journal_submission', - ) and not request.user.is_staff: + if ( + request.journal.get_setting( + "general", + "disable_journal_submission", + ) + and not request.user.is_staff + ): raise PermissionDenied( - _('Submission is disabled for this journal.'), + _("Submission is disabled for this journal."), ) return func(request, *args, **kwargs) @@ -36,17 +39,17 @@ def submission_is_enabled_wrapper(request, *args, **kwargs): def funding_is_enabled(func): - """ Test if funding is enabled before returning the wrapped view + """Test if funding is enabled before returning the wrapped view :param func: the function to callback from the decorator :return: either the function call or raises an Http404 """ + @wraps(func) def funding_is_enabled(request, *args, **kwargs): if "article_id" in kwargs: - article_id = kwargs['article_id'] - article = models.Article.get_article( - request.journal, 'id', article_id) + article_id = kwargs["article_id"] + article = models.Article.get_article(request.journal, "id", article_id) # Staff and editors can bypass this requirement. if ( diff --git a/src/submission/encoding.py b/src/submission/encoding.py index ad690e73cc..7d088c01d8 100644 --- a/src/submission/encoding.py +++ b/src/submission/encoding.py @@ -1,4 +1,3 @@ - __copyright__ = "Copyright 2022 Birkbeck, University of London" __author__ = "Birkbeck Centre for Technology and Publishing" __license__ = "AGPL v3" @@ -9,7 +8,7 @@ def encode_article_as_bibtex(article): context = {"article": article} - template = get_template('encoding/article_bibtex.bib') + template = get_template("encoding/article_bibtex.bib") rendered = template.render(context) return rendered @@ -17,7 +16,7 @@ def encode_article_as_bibtex(article): def encode_article_as_ris(article): context = {"article": article} - template = get_template('encoding/article_ris.ris') + template = get_template("encoding/article_ris.ris") rendered = template.render(context) return rendered diff --git a/src/submission/forms.py b/src/submission/forms.py index 604012de7f..54f65ff1f9 100755 --- a/src/submission/forms.py +++ b/src/submission/forms.py @@ -23,52 +23,50 @@ class PublisherNoteForm(forms.ModelForm): - class Meta: model = models.PublisherNote - fields = ('text',) + fields = ("text",) class ArticleStart(forms.ModelForm): - class Meta: model = models.Article fields = ( - 'publication_fees', - 'submission_requirements', - 'copyright_notice', - 'competing_interests', + "publication_fees", + "submission_requirements", + "copyright_notice", + "competing_interests", ) def __init__(self, *args, **kwargs): - journal = kwargs.pop('journal', False) + journal = kwargs.pop("journal", False) super(ArticleStart, self).__init__(*args, **kwargs) - self.fields['competing_interests'].label = '' + self.fields["competing_interests"].label = "" if not journal.submissionconfiguration.publication_fees: - self.fields.pop('publication_fees') + self.fields.pop("publication_fees") else: - self.fields['publication_fees'].required = True + self.fields["publication_fees"].required = True if not journal.submissionconfiguration.submission_check: - self.fields.pop('submission_requirements') + self.fields.pop("submission_requirements") else: - self.fields['submission_requirements'].required = True + self.fields["submission_requirements"].required = True if not journal.submissionconfiguration.copyright_notice: - self.fields.pop('copyright_notice') + self.fields.pop("copyright_notice") else: - self.fields['copyright_notice'].required = True + self.fields["copyright_notice"].required = True copyright_label = setting_handler.get_setting( - 'general', - 'copyright_submission_label', + "general", + "copyright_submission_label", journal, ).processed_value - self.fields['copyright_notice'].label = copyright_label + self.fields["copyright_notice"].label = copyright_label if not journal.submissionconfiguration.competing_interests: - self.fields.pop('competing_interests') + self.fields.pop("competing_interests") class ArticleInfo(KeywordModelForm, JanewayTranslationModelForm): @@ -77,15 +75,28 @@ class ArticleInfo(KeywordModelForm, JanewayTranslationModelForm): class Meta: model = models.Article fields = ( - 'title', 'subtitle', 'abstract', 'non_specialist_summary', - 'language', 'section', 'license', 'primary_issue', - 'article_number', 'is_remote', 'remote_url', 'peer_reviewed', - 'first_page', 'last_page', 'page_numbers', 'total_pages', - 'custom_how_to_cite', 'rights', + "title", + "subtitle", + "abstract", + "non_specialist_summary", + "language", + "section", + "license", + "primary_issue", + "article_number", + "is_remote", + "remote_url", + "peer_reviewed", + "first_page", + "last_page", + "page_numbers", + "total_pages", + "custom_how_to_cite", + "rights", ) widgets = { - 'title': forms.TextInput(attrs={'placeholder': _('Title')}), - 'subtitle': forms.TextInput(attrs={'placeholder': _('Subtitle')}), + "title": forms.TextInput(attrs={"placeholder": _("Title")}), + "subtitle": forms.TextInput(attrs={"placeholder": _("Subtitle")}), } def __init__(self, *args, **kwargs): @@ -99,19 +110,19 @@ def __init__(self, *args, **kwargs): fields that are disabled by SubmissionConfiguration or overwrite their saving. """ - elements = kwargs.pop('additional_fields', None) - submission_summary = kwargs.pop('submission_summary', None) - journal = kwargs.pop('journal', None) - self.pop_disabled_fields = kwargs.pop('pop_disabled_fields', True) - editor_view = kwargs.pop('editor_view', False) + elements = kwargs.pop("additional_fields", None) + submission_summary = kwargs.pop("submission_summary", None) + journal = kwargs.pop("journal", None) + self.pop_disabled_fields = kwargs.pop("pop_disabled_fields", True) + editor_view = kwargs.pop("editor_view", False) super(ArticleInfo, self).__init__(*args, **kwargs) # Flag labels for translation for field in self.fields.values(): field.label = _(field.label) - if 'instance' in kwargs: - article = kwargs['instance'] + if "instance" in kwargs: + article = kwargs["instance"] section_queryset = models.Section.objects.filter( journal=article.journal, ) @@ -125,84 +136,92 @@ def __init__(self, *args, **kwargs): license_queryset = license_queryset.filter( available_for_submission=self.FILTER_PUBLIC_FIELDS, ) - self.fields['section'].queryset = section_queryset - self.fields['license'].queryset = license_queryset + self.fields["section"].queryset = section_queryset + self.fields["license"].queryset = license_queryset - self.fields['section'].required = True - self.fields['license'].required = True - self.fields['primary_issue'].queryset = article.issues.all() + self.fields["section"].required = True + self.fields["license"].required = True + self.fields["primary_issue"].queryset = article.issues.all() abstracts_required = article.journal.get_setting( - 'general', - 'abstract_required', + "general", + "abstract_required", ) if abstracts_required: - self.fields['abstract'].required = True + self.fields["abstract"].required = True if submission_summary: - self.fields['non_specialist_summary'].required = True + self.fields["non_specialist_summary"].required = True # Pop fields based on journal.submissionconfiguration if journal and self.pop_disabled_fields: if not journal.submissionconfiguration.subtitle: - self.fields.pop('subtitle') + self.fields.pop("subtitle") if not journal.submissionconfiguration.abstract: - self.fields.pop('abstract') + self.fields.pop("abstract") if not journal.submissionconfiguration.language: - self.fields.pop('language') + self.fields.pop("language") if not journal.submissionconfiguration.license: - self.fields.pop('license') + self.fields.pop("license") if not journal.submissionconfiguration.keywords: - self.fields.pop('keywords') + self.fields.pop("keywords") if not journal.submissionconfiguration.section: - self.fields.pop('section') + self.fields.pop("section") # Add additional fields if elements: for element in elements: - if element.kind == 'text': + if element.kind == "text": self.fields[element.name] = forms.CharField( - widget=forms.TextInput(attrs={'div_class': element.width}), - required=element.required) - elif element.kind == 'textarea': + widget=forms.TextInput(attrs={"div_class": element.width}), + required=element.required, + ) + elif element.kind == "textarea": self.fields[element.name] = forms.CharField( - widget=TinyMCE(), - required=element.required, + widget=TinyMCE(), + required=element.required, ) - elif element.kind == 'date': + elif element.kind == "date": self.fields[element.name] = forms.CharField( widget=HTMLDateInput( - attrs={'div_class': element.width}, + attrs={"div_class": element.width}, ), - required=element.required) + required=element.required, + ) - elif element.kind == 'select': + elif element.kind == "select": choices = render_choices(element.choices) self.fields[element.name] = forms.ChoiceField( - widget=forms.Select(attrs={'div_class': element.width}), choices=choices, - required=element.required) + widget=forms.Select(attrs={"div_class": element.width}), + choices=choices, + required=element.required, + ) - elif element.kind == 'email': + elif element.kind == "email": self.fields[element.name] = forms.EmailField( - widget=forms.TextInput(attrs={'div_class': element.width}), - required=element.required) - elif element.kind == 'check': + widget=forms.TextInput(attrs={"div_class": element.width}), + required=element.required, + ) + elif element.kind == "check": self.fields[element.name] = forms.BooleanField( - widget=forms.CheckboxInput(attrs={'is_checkbox': True}), - required=element.required) + widget=forms.CheckboxInput(attrs={"is_checkbox": True}), + required=element.required, + ) self.fields[element.name].help_text = element.help_text self.fields[element.name].label = element.name if article: try: - check_for_answer = models.FieldAnswer.objects.get(field=element, article=article) + check_for_answer = models.FieldAnswer.objects.get( + field=element, article=article + ) self.fields[element.name].initial = check_for_answer.answer except models.FieldAnswer.DoesNotExist: pass @@ -222,11 +241,15 @@ def save(self, commit=True, request=None): answer = request.POST.get(field.name, None) if answer: try: - field_answer = models.FieldAnswer.objects.get(article=article, field=field) + field_answer = models.FieldAnswer.objects.get( + article=article, field=field + ) field_answer.answer = answer field_answer.save() except models.FieldAnswer.DoesNotExist: - field_answer = models.FieldAnswer.objects.create(article=article, field=field, answer=answer) + field_answer = models.FieldAnswer.objects.create( + article=article, field=field, answer=answer + ) if self.pop_disabled_fields: request.journal.submissionconfiguration.handle_defaults(article) @@ -248,73 +271,79 @@ class EditorArticleInfoSubmit(ArticleInfo): def __init__(self, *args, **kwargs): super(EditorArticleInfoSubmit, self).__init__(*args, **kwargs) - if self.fields.get('section'): - self.fields['section'].label_from_instance = lambda obj: obj.display_name_public_submission - self.fields['section'].help_text = "As an editor you will see all " \ - "sections even if they are " \ - "closed for public submission" + if self.fields.get("section"): + self.fields["section"].label_from_instance = ( + lambda obj: obj.display_name_public_submission + ) + self.fields["section"].help_text = ( + "As an editor you will see all " + "sections even if they are " + "closed for public submission" + ) class EditArticleMetadata(ArticleInfo): class Meta(ArticleInfo.Meta): - fields = ArticleInfo.Meta.fields + ('competing_interests',) + fields = ArticleInfo.Meta.fields + ("competing_interests",) class AuthorForm(forms.ModelForm): - class Meta: model = core_models.Account exclude = ( - 'date_joined', - 'activation_code' - 'date_confirmed' - 'confirmation_code' - 'reset_code' - 'reset_code_validated' - 'roles' - 'interest' - 'is_active' - 'is_staff' - 'is_admin' - 'password', - 'username', - 'roles', + "date_joined", + "activation_code" + "date_confirmed" + "confirmation_code" + "reset_code" + "reset_code_validated" + "roles" + "interest" + "is_active" + "is_staff" + "is_admin" + "password", + "username", + "roles", ) widgets = { - 'first_name': forms.TextInput(attrs={'placeholder': 'First name'}), - 'middle_name': forms.TextInput(attrs={'placeholder': _('Middle name')}), - 'last_name': forms.TextInput(attrs={'placeholder': _('Last name')}), - 'biography': forms.Textarea( - attrs={'placeholder': _('Enter biography here')}), - 'institution': forms.TextInput(attrs={'placeholder': _('Institution')}), - 'department': forms.TextInput(attrs={'placeholder': _('Department')}), - 'twitter': forms.TextInput(attrs={'placeholder': _('Twitter handle')}), - 'linkedin': forms.TextInput(attrs={'placeholder': _('LinkedIn profile')}), - 'impactstory': forms.TextInput(attrs={'placeholder': _('ImpactStory profile')}), - 'orcid': forms.TextInput(attrs={'placeholder': _('ORCID ID')}), - 'email': forms.TextInput(attrs={'placeholder': _('Email address')}), + "first_name": forms.TextInput(attrs={"placeholder": "First name"}), + "middle_name": forms.TextInput(attrs={"placeholder": _("Middle name")}), + "last_name": forms.TextInput(attrs={"placeholder": _("Last name")}), + "biography": forms.Textarea( + attrs={"placeholder": _("Enter biography here")} + ), + "institution": forms.TextInput(attrs={"placeholder": _("Institution")}), + "department": forms.TextInput(attrs={"placeholder": _("Department")}), + "twitter": forms.TextInput(attrs={"placeholder": _("Twitter handle")}), + "linkedin": forms.TextInput(attrs={"placeholder": _("LinkedIn profile")}), + "impactstory": forms.TextInput( + attrs={"placeholder": _("ImpactStory profile")} + ), + "orcid": forms.TextInput(attrs={"placeholder": _("ORCID ID")}), + "email": forms.TextInput(attrs={"placeholder": _("Email address")}), } def __init__(self, *args, **kwargs): super(AuthorForm, self).__init__(*args, **kwargs) - self.fields['password'].required = False - self.fields['first_name'].required = True - self.fields['last_name'].required = True + self.fields["password"].required = False + self.fields["first_name"].required = True + self.fields["last_name"].required = True def clean_orcid(self): - orcid_string = self.cleaned_data.get('orcid') + orcid_string = self.cleaned_data.get("orcid") try: return utility_clean_orcid(orcid_string) except ValueError: self.add_error( - 'orcid', - 'An ORCID must be entered in the pattern ' - 'https://orcid.org/0000-0000-0000-0000 or' - ' 0000-0000-0000-0000. You can find out ' - 'about valid ORCID patterns on the ORCID support site: ' - 'https://support.orcid.org/hc/en-us/articles/' - '360006897674-Structure-of-the-ORCID-Identifier', + "orcid", + "An ORCID must be entered in the pattern " + "https://orcid.org/0000-0000-0000-0000 or" + " 0000-0000-0000-0000. You can find out " + "about valid ORCID patterns on the ORCID support site: " + "https://support.orcid.org/hc/en-us/articles/" + "360006897674-Structure-of-the-ORCID-Identifier", ) return orcid_string @@ -322,24 +351,23 @@ def clean_orcid(self): class SubmissionCommentsForm(forms.ModelForm): class Meta: model = models.Article - fields = ('comments_editor',) + fields = ("comments_editor",) labels = { - 'comments_editor': '', + "comments_editor": "", } class FileDetails(forms.ModelForm): - class Meta: model = core_models.File fields = ( - 'label', - 'description', + "label", + "description", ) def __init__(self, *args, **kwargs): super(FileDetails, self).__init__(*args, **kwargs) - self.fields['label'].required = True + self.fields["label"].required = True class EditFrozenAuthor(forms.ModelForm): @@ -368,19 +396,19 @@ def __init__(self, *args, **kwargs): class Meta: model = models.FrozenAuthor fields = ( - 'name_prefix', - 'first_name', - 'middle_name', - 'last_name', - 'name_suffix', - 'institution', - 'department', - 'frozen_biography', - 'country', - 'is_corporate', - 'frozen_email', - 'frozen_orcid', - 'display_email', + "name_prefix", + "first_name", + "middle_name", + "last_name", + "name_suffix", + "institution", + "department", + "frozen_biography", + "country", + "is_corporate", + "frozen_email", + "frozen_orcid", + "display_email", ) def save(self, commit=True, *args, **kwargs): @@ -389,7 +417,8 @@ def save(self, commit=True, *args, **kwargs): try: # Associate with account if one exists account = core_models.Account.objects.get( - username=obj.frozen_email.lower()) + username=obj.frozen_email.lower() + ) obj.author = account obj.frozen_email = None except core_models.Account.DoesNotExist: @@ -398,123 +427,114 @@ def save(self, commit=True, *args, **kwargs): return obj def clean_frozen_orcid(self): - orcid_string = self.cleaned_data.get('frozen_orcid') + orcid_string = self.cleaned_data.get("frozen_orcid") try: return utility_clean_orcid(orcid_string) except ValueError: self.add_error( - 'frozen_orcid', - 'An ORCID must be entered in the pattern ' - 'https://orcid.org/0000-0000-0000-0000 or' - ' 0000-0000-0000-0000. You can find out ' - 'about valid ORCID patterns on the ORCID support site: ' - 'https://support.orcid.org/hc/en-us/articles/' - '360006897674-Structure-of-the-ORCID-Identifier', + "frozen_orcid", + "An ORCID must be entered in the pattern " + "https://orcid.org/0000-0000-0000-0000 or" + " 0000-0000-0000-0000. You can find out " + "about valid ORCID patterns on the ORCID support site: " + "https://support.orcid.org/hc/en-us/articles/" + "360006897674-Structure-of-the-ORCID-Identifier", ) return orcid_string class IdentifierForm(forms.ModelForm): - class Meta: model = ident_models.Identifier fields = ( - 'id_type', - 'identifier', - 'enabled', + "id_type", + "identifier", + "enabled", ) class FieldForm(forms.ModelForm): - class Meta: model = models.Field exclude = ( - 'journal', - 'press', + "journal", + "press", ) class LicenseForm(forms.ModelForm): - class Meta: model = models.Licence exclude = ( - 'journal', - 'press', + "journal", + "press", ) class ConfiguratorForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super(ConfiguratorForm, self).__init__(*args, **kwargs) - self.fields['default_section'].queryset = models.Section.objects.filter( + self.fields["default_section"].queryset = models.Section.objects.filter( journal=self.instance.journal, ) - self.fields[ - 'default_license'].queryset = models.Licence.objects.filter( + self.fields["default_license"].queryset = models.Licence.objects.filter( journal=self.instance.journal, ) def clean(self): cleaned_data = super().clean() - license = cleaned_data.get('license', False) - section = cleaned_data.get('section', False) - language = cleaned_data.get('language', False) + license = cleaned_data.get("license", False) + section = cleaned_data.get("section", False) + language = cleaned_data.get("language", False) - default_license = cleaned_data.get('default_license', None) - default_section = cleaned_data.get('default_section', None) - default_language = cleaned_data.get('default_language', None) + default_license = cleaned_data.get("default_license", None) + default_section = cleaned_data.get("default_section", None) + default_language = cleaned_data.get("default_language", None) if not license and not default_license: self.add_error( - 'default_license', - 'If license is unset you must select a default license.', + "default_license", + "If license is unset you must select a default license.", ) if not section and not default_section: self.add_error( - 'default_section', - 'If section is unset you must select a default section.', + "default_section", + "If section is unset you must select a default section.", ) if not language and not default_language: self.add_error( - 'default_language', - 'If language is unset you must select a default language.' + "default_language", + "If language is unset you must select a default language.", ) class Meta: model = models.SubmissionConfiguration - exclude = ( - 'journal', - ) + exclude = ("journal",) class ProjectedIssueForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super(ProjectedIssueForm, self).__init__(*args, **kwargs) - self.fields['projected_issue'].queryset = self.instance.journal.issue_set.all() + self.fields["projected_issue"].queryset = self.instance.journal.issue_set.all() class Meta: model = models.Article - fields = ('projected_issue',) + fields = ("projected_issue",) class ArticleFundingForm(forms.ModelForm): - class Meta: model = models.ArticleFunding - fields = ('name', 'fundref_id', 'funding_id', 'funding_statement') + fields = ("name", "fundref_id", "funding_id", "funding_statement") widgets = { - 'funding_statement': TinyMCE(), + "funding_statement": TinyMCE(), } def __init__(self, *args, **kwargs): - self.article = kwargs.pop('article', None) + self.article = kwargs.pop("article", None) super().__init__(*args, **kwargs) def save(self, commit=True, *args, **kwargs): @@ -531,13 +551,15 @@ def utility_clean_orcid(orcid): Utility function that cleans an ORCID ID. """ if orcid: - orcid_regex = re.compile('([0]{3})([0,9]{1})-([0-9]{4})-([0-9]{4})-([0-9]{3})([0-9X]{1})') + orcid_regex = re.compile( + "([0]{3})([0,9]{1})-([0-9]{4})-([0-9]{4})-([0-9]{3})([0-9X]{1})" + ) result = orcid_regex.search(orcid) if result: return result.group(0) else: - raise ValueError('ORCID is not valid.') + raise ValueError("ORCID is not valid.") # ORCID is None. return orcid @@ -546,14 +568,12 @@ def utility_clean_orcid(orcid): class PubDateForm(forms.ModelForm): class Meta: model = models.Article - fields = ('date_published',) + fields = ("date_published",) def save(self, commit=True): article = super().save(commit=commit) if commit: - article.fixedpubcheckitems.set_pub_date = bool( - article.date_published - ) + article.fixedpubcheckitems.set_pub_date = bool(article.date_published) article.fixedpubcheckitems.save() article.save() return article diff --git a/src/submission/logic.py b/src/submission/logic.py index 9b6604efd4..7011101a63 100755 --- a/src/submission/logic.py +++ b/src/submission/logic.py @@ -24,25 +24,27 @@ def add_self_as_author(user, article): def add_user_as_author(user, article, give_role=True): - """ Assigns the given user as an author of the paper + """Assigns the given user as an author of the paper :param user: An instance of core.models.Account :param article: An instance of submission.models.Article :param give_role: If true, the user is given the author role in the journal """ if give_role: submission_requires_authorisation = article.journal.get_setting( - group_name='general', - setting_name='limit_access_to_submission', + group_name="general", + setting_name="limit_access_to_submission", ) - if submission_requires_authorisation and not user.check_role(article.journal, 'author'): + if submission_requires_authorisation and not user.check_role( + article.journal, "author" + ): role = core_models.Role.objects.get( - slug='author', + slug="author", ) core_models.AccessRequest.objects.get_or_create( journal=article.journal, user=user, role=role, - text='Automatic request as author added to an article.', + text="Automatic request as author added to an article.", ) else: user.add_account_role("author", article.journal) @@ -51,7 +53,7 @@ def add_user_as_author(user, article, give_role=True): models.ArticleAuthorOrder.objects.get_or_create( article=article, author=user, - defaults={'order': article.next_author_sort()}, + defaults={"order": article.next_author_sort()}, ) return user @@ -65,30 +67,35 @@ def check_author_exists(email): def get_author(request, article): - author_id = request.GET.get('author') + author_id = request.GET.get("author") frozen_authors = article.frozen_authors() try: author = frozen_authors.get(pk=author_id) - return [author, 'author'] + return [author, "author"] except core_models.Account.DoesNotExist: return [None, None] def get_agreement_text(journal): - pub_fees = setting_handler.get_setting('general', 'publication_fees', journal).value - sub_check = setting_handler.get_setting('general', 'submission_checklist', journal).value - copy_notice = setting_handler.get_setting('general', 'copyright_notice', journal).value + pub_fees = setting_handler.get_setting("general", "publication_fees", journal).value + sub_check = setting_handler.get_setting( + "general", "submission_checklist", journal + ).value + copy_notice = setting_handler.get_setting( + "general", "copyright_notice", journal + ).value return "{0}\n\n{1}\n\n{2}".format(pub_fees, sub_check, copy_notice) def check_file(uploaded_file, request, form): - if not uploaded_file: - form.add_error(None, 'You must select a file.') + form.add_error(None, "You must select a file.") return False - submission_formats = setting_handler.get_setting('general', 'limit_manuscript_types', request.journal).value + submission_formats = setting_handler.get_setting( + "general", "limit_manuscript_types", request.journal + ).value if submission_formats: mime = files.guess_mime(str(uploaded_file.name)) @@ -96,7 +103,10 @@ def check_file(uploaded_file, request, form): if mime in files.EDITABLE_FORMAT: return True else: - form.add_error(None, _('You must upload a file that is either a Doc, Docx, RTF or ODT.')) + form.add_error( + None, + _("You must upload a file that is either a Doc, Docx, RTF or ODT."), + ) return False else: return True @@ -106,33 +116,40 @@ def get_text(soup, to_find): try: return soup.find(to_find).text except AttributeError: - return '' + return "" def parse_authors(soup): - authors = soup.find_all('contrib') + authors = soup.find_all("contrib") author_list = [] for author in authors: - first_name = get_text(author, 'given-names') - last_name = get_text(author, 'surname') - email = get_text(author, 'email') + first_name = get_text(author, "given-names") + last_name = get_text(author, "surname") + email = get_text(author, "email") try: - aff_id = author.find('xref').get('rid', None) - aff = author.find('aff', attrs={'id': aff_id}).text + aff_id = author.find("xref").get("rid", None) + aff = author.find("aff", attrs={"id": aff_id}).text except AttributeError: - aff = get_text(author, 'aff') - - author_list.append({'first_name': first_name, 'last_name': last_name, 'email': email, 'institution': aff}) + aff = get_text(author, "aff") + + author_list.append( + { + "first_name": first_name, + "last_name": last_name, + "email": email, + "institution": aff, + } + ) return author_list def add_keywords(soup, article): - keywords = soup.find_all('kwd') + keywords = soup.find_all("kwd") for keyword in keywords: - if keyword.text not in [None, '', ' ']: + if keyword.text not in [None, "", " "]: obj, c = models.Keyword.objects.get_or_create( word=str(keyword.text).strip(), ) @@ -141,18 +158,20 @@ def add_keywords(soup, article): def import_from_jats_xml(path, journal, first_author_is_primary=False): with open(path) as file: - soup = BeautifulSoup(file, 'lxml-xml') - title = get_text(soup, 'article-title') - abstract = get_text(soup, 'abstract') + soup = BeautifulSoup(file, "lxml-xml") + title = get_text(soup, "article-title") + abstract = get_text(soup, "abstract") authors = parse_authors(soup) - section = get_text(soup, 'subj-group') + section = get_text(soup, "subj-group") try: - pub_date = soup.find('pub-date').get('iso-8601-date') + pub_date = soup.find("pub-date").get("iso-8601-date") except AttributeError: pub_date = None - section_obj, created = models.Section.objects.get_or_create(name=section, journal=journal) + section_obj, created = models.Section.objects.get_or_create( + name=section, journal=journal + ) article = models.Article.objects.create( title=title, @@ -163,24 +182,25 @@ def import_from_jats_xml(path, journal, first_author_is_primary=False): ) for author in authors: - if not author.get('email') or author.get('email') == '': - author['email'] = '{first}.{last}@journal.com'.format(first=author.get('first_name'), - last=author.get('last_name')) + if not author.get("email") or author.get("email") == "": + author["email"] = "{first}.{last}@journal.com".format( + first=author.get("first_name"), last=author.get("last_name") + ) try: - author = core_models.Account.objects.get(Q(email=author['email']) | Q(username=author['email'])) + author = core_models.Account.objects.get( + Q(email=author["email"]) | Q(username=author["email"]) + ) except core_models.Account.DoesNotExist: author = core_models.Account.objects.create( - email=author['email'], - username=author['email'], - first_name=author['first_name'], - last_name=author['last_name'], - institution=author['institution'] + email=author["email"], + username=author["email"], + first_name=author["first_name"], + last_name=author["last_name"], + institution=author["institution"], ) article.authors.add(author) models.ArticleAuthorOrder.objects.create( - article=article, - author=author, - order=article.next_author_sort() + article=article, author=author, order=article.next_author_sort() ) if first_author_is_primary and article.authors.all(): @@ -201,7 +221,9 @@ def get_current_field(request, field_id): """ if field_id: if request.journal: - field = get_object_or_404(models.Field, pk=field_id, journal=request.journal) + field = get_object_or_404( + models.Field, pk=field_id, journal=request.journal + ) else: field = get_object_or_404(models.Field, pk=field_id, press=request.press) else: @@ -241,7 +263,7 @@ def save_field(request, form): new_field.press = request.press new_field.save() - messages.add_message(request, messages.SUCCESS, 'Field saved.') + messages.add_message(request, messages.SUCCESS, "Field saved.") return new_field @@ -252,10 +274,12 @@ def delete_field(request): :return: None, adds a Message obejct to request """ - delete_id = request.POST.get('delete') + delete_id = request.POST.get("delete") field_to_delete = get_current_field(request, delete_id) field_to_delete.delete() - messages.add_message(request, messages.SUCCESS, 'Field deleted. Existing answers will remain intact.') + messages.add_message( + request, messages.SUCCESS, "Field deleted. Existing answers will remain intact." + ) def order_fields(request, fields): @@ -266,7 +290,7 @@ def order_fields(request, fields): :return: None """ - ids = [int(_id) for _id in request.POST.getlist('order[]')] + ids = [int(_id) for _id in request.POST.getlist("order[]")] for field in fields: order = ids.index(field.pk) @@ -275,13 +299,11 @@ def order_fields(request, fields): def save_author_order(request, article): - author_pks = [int(pk) for pk in request.POST.getlist('authors[]')] + author_pks = [int(pk) for pk in request.POST.getlist("authors[]")] for author in article.authors.all(): order = author_pks.index(author.pk) author_order, c = models.ArticleAuthorOrder.objects.get_or_create( - article=article, - author=author, - defaults={'order': order} + article=article, author=author, defaults={"order": order} ) if not c: diff --git a/src/submission/migrations/0001_initial.py b/src/submission/migrations/0001_initial.py index 823a18cfc1..d094442865 100755 --- a/src/submission/migrations/0001_initial.py +++ b/src/submission/migrations/0001_initial.py @@ -11,218 +11,1005 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('journal', '0001_initial'), - ('core', '0001_initial'), + ("journal", "0001_initial"), + ("core", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Article', + name="Article", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=300)), - ('subtitle', models.CharField(blank=True, max_length=300, null=True)), - ('abstract', models.TextField(blank=True)), - ('language', models.CharField(blank=True, choices=[('eng', 'English'), ('abk', 'Abkhazian'), ('ace', 'Achinese'), ('ach', 'Acoli'), ('ada', 'Adangme'), ('ady', 'Adyghe; Adygei'), ('aar', 'Afar'), ('afh', 'Afrihili'), ('afr', 'Afrikaans'), ('afa', 'Afro-Asiatic languages'), ('ain', 'Ainu'), ('aka', 'Akan'), ('akk', 'Akkadian'), ('sqi', 'Albanian'), ('ale', 'Aleut'), ('alg', 'Algonquian languages'), ('tut', 'Altaic languages'), ('amh', 'Amharic'), ('anp', 'Angika'), ('apa', 'Apache languages'), ('ara', 'Arabic'), ('arg', 'Aragonese'), ('arp', 'Arapaho'), ('arw', 'Arawak'), ('hye', 'Armenian'), ('rup', 'Aromanian; Arumanian; Macedo-Romanian'), ('art', 'Artificial languages'), ('asm', 'Assamese'), ('ast', 'Asturian; Bable; Leonese; Asturleonese'), ('ath', 'Athapascan languages'), ('aus', 'Australian languages'), ('map', 'Austronesian languages'), ('ava', 'Avaric'), ('ave', 'Avestan'), ('awa', 'Awadhi'), ('aym', 'Aymara'), ('aze', 'Azerbaijani'), ('ban', 'Balinese'), ('bat', 'Baltic languages'), ('bal', 'Baluchi'), ('bam', 'Bambara'), ('bai', 'Bamileke languages'), ('bad', 'Banda languages'), ('bnt', 'Bantu languages'), ('bas', 'Basa'), ('bak', 'Bashkir'), ('eus', 'Basque'), ('btk', 'Batak languages'), ('bej', 'Beja; Bedawiyet'), ('bel', 'Belarusian'), ('bem', 'Bemba'), ('ben', 'Bengali'), ('ber', 'Berber languages'), ('bho', 'Bhojpuri'), ('bih', 'Bihari languages'), ('bik', 'Bikol'), ('bin', 'Bini; Edo'), ('bis', 'Bislama'), ('byn', 'Blin; Bilin'), ('zbl', 'Blissymbols; Blissymbolics; Bliss'), ('nob', 'Bokmål, Norwegian; Norwegian Bokmål'), ('bos', 'Bosnian'), ('bra', 'Braj'), ('bre', 'Breton'), ('bug', 'Buginese'), ('bul', 'Bulgarian'), ('bua', 'Buriat'), ('mya', 'Burmese'), ('cad', 'Caddo'), ('cat', 'Catalan; Valencian'), ('cau', 'Caucasian languages'), ('ceb', 'Cebuano'), ('cel', 'Celtic languages'), ('cai', 'Central American Indian languages'), ('khm', 'Central Khmer'), ('chg', 'Chagatai'), ('cmc', 'Chamic languages'), ('cha', 'Chamorro'), ('che', 'Chechen'), ('chr', 'Cherokee'), ('chy', 'Cheyenne'), ('chb', 'Chibcha'), ('nya', 'Chichewa; Chewa; Nyanja'), ('zho', 'Chinese'), ('chn', 'Chinook jargon'), ('chp', 'Chipewyan; Dene Suline'), ('cho', 'Choctaw'), ('chu', 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), ('chk', 'Chuukese'), ('chv', 'Chuvash'), ('nwc', 'Classical Newari; Old Newari; Classical Nepal Bhasa'), ('syc', 'Classical Syriac'), ('cop', 'Coptic'), ('cor', 'Cornish'), ('cos', 'Corsican'), ('cre', 'Cree'), ('mus', 'Creek'), ('crp', 'Creoles and pidgins'), ('cpe', 'Creoles and pidgins, English based'), ('cpf', 'Creoles and pidgins, French-based'), ('cpp', 'Creoles and pidgins, Portuguese-based'), ('crh', 'Crimean Tatar; Crimean Turkish'), ('hrv', 'Croatian'), ('cus', 'Cushitic languages'), ('ces', 'Czech'), ('dak', 'Dakota'), ('dan', 'Danish'), ('dar', 'Dargwa'), ('del', 'Delaware'), ('din', 'Dinka'), ('div', 'Divehi; Dhivehi; Maldivian'), ('doi', 'Dogri'), ('dgr', 'Dogrib'), ('dra', 'Dravidian languages'), ('dua', 'Duala'), ('dum', 'Dutch, Middle (ca. 1050-1350)'), ('nld', 'Dutch; Flemish'), ('dyu', 'Dyula'), ('dzo', 'Dzongkha'), ('frs', 'Eastern Frisian'), ('efi', 'Efik'), ('egy', 'Egyptian (Ancient)'), ('eka', 'Ekajuk'), ('elx', 'Elamite'), ('enm', 'English, Middle (1100-1500)'), ('ang', 'English, Old (ca. 450-1100)'), ('myv', 'Erzya'), ('epo', 'Esperanto'), ('est', 'Estonian'), ('ewe', 'Ewe'), ('ewo', 'Ewondo'), ('fan', 'Fang'), ('fat', 'Fanti'), ('fao', 'Faroese'), ('fij', 'Fijian'), ('fil', 'Filipino; Pilipino'), ('fin', 'Finnish'), ('fiu', 'Finno-Ugrian languages'), ('fon', 'Fon'), ('fra', 'French'), ('frm', 'French, Middle (ca. 1400-1600)'), ('fro', 'French, Old (842-ca. 1400)'), ('fur', 'Friulian'), ('ful', 'Fulah'), ('gaa', 'Ga'), ('gla', 'Gaelic; Scottish Gaelic'), ('car', 'Galibi Carib'), ('glg', 'Galician'), ('lug', 'Ganda'), ('gay', 'Gayo'), ('gba', 'Gbaya'), ('gez', 'Geez'), ('kat', 'Georgian'), ('deu', 'German'), ('gmh', 'German, Middle High (ca. 1050-1500)'), ('goh', 'German, Old High (ca. 750-1050)'), ('gem', 'Germanic languages'), ('gil', 'Gilbertese'), ('gon', 'Gondi'), ('gor', 'Gorontalo'), ('got', 'Gothic'), ('grb', 'Grebo'), ('grc', 'Greek, Ancient (to 1453)'), ('ell', 'Greek, Modern (1453-)'), ('grn', 'Guarani'), ('guj', 'Gujarati'), ('gwi', "Gwich'in"), ('hai', 'Haida'), ('hat', 'Haitian; Haitian Creole'), ('hau', 'Hausa'), ('haw', 'Hawaiian'), ('heb', 'Hebrew'), ('her', 'Herero'), ('hil', 'Hiligaynon'), ('him', 'Himachali languages; Western Pahari languages'), ('hin', 'Hindi'), ('hmo', 'Hiri Motu'), ('hit', 'Hittite'), ('hmn', 'Hmong; Mong'), ('hun', 'Hungarian'), ('hup', 'Hupa'), ('iba', 'Iban'), ('isl', 'Icelandic'), ('ido', 'Ido'), ('ibo', 'Igbo'), ('ijo', 'Ijo languages'), ('ilo', 'Iloko'), ('smn', 'Inari Sami'), ('inc', 'Indic languages'), ('ine', 'Indo-European languages'), ('ind', 'Indonesian'), ('inh', 'Ingush'), ('ina', 'Interlingua (International Auxiliary Language Association)'), ('ile', 'Interlingue; Occidental'), ('iku', 'Inuktitut'), ('ipk', 'Inupiaq'), ('ira', 'Iranian languages'), ('gle', 'Irish'), ('mga', 'Irish, Middle (900-1200)'), ('sga', 'Irish, Old (to 900)'), ('iro', 'Iroquoian languages'), ('ita', 'Italian'), ('jpn', 'Japanese'), ('jav', 'Javanese'), ('jrb', 'Judeo-Arabic'), ('jpr', 'Judeo-Persian'), ('kbd', 'Kabardian'), ('kab', 'Kabyle'), ('kac', 'Kachin; Jingpho'), ('kal', 'Kalaallisut; Greenlandic'), ('xal', 'Kalmyk; Oirat'), ('kam', 'Kamba'), ('kan', 'Kannada'), ('kau', 'Kanuri'), ('kaa', 'Kara-Kalpak'), ('krc', 'Karachay-Balkar'), ('krl', 'Karelian'), ('kar', 'Karen languages'), ('kas', 'Kashmiri'), ('csb', 'Kashubian'), ('kaw', 'Kawi'), ('kaz', 'Kazakh'), ('kha', 'Khasi'), ('khi', 'Khoisan languages'), ('kho', 'Khotanese;Sakan'), ('kik', 'Kikuyu; Gikuyu'), ('kmb', 'Kimbundu'), ('kin', 'Kinyarwanda'), ('kir', 'Kirghiz; Kyrgyz'), ('tlh', 'Klingon; tlhIngan-Hol'), ('kom', 'Komi'), ('kon', 'Kongo'), ('kok', 'Konkani'), ('kor', 'Korean'), ('kos', 'Kosraean'), ('kpe', 'Kpelle'), ('kro', 'Kru languages'), ('kua', 'Kuanyama; Kwanyama'), ('kum', 'Kumyk'), ('kur', 'Kurdish'), ('kru', 'Kurukh'), ('kut', 'Kutenai'), ('lad', 'Ladino'), ('lah', 'Lahnda'), ('lam', 'Lamba'), ('day', 'Land Dayak languages'), ('lao', 'Lao'), ('lat', 'Latin'), ('lav', 'Latvian'), ('lez', 'Lezghian'), ('lim', 'Limburgan; Limburger; Limburgish'), ('lin', 'Lingala'), ('lit', 'Lithuanian'), ('jbo', 'Lojban'), ('nds', 'Low German; Low Saxon; German, Low; Saxon, Low'), ('dsb', 'Lower Sorbian'), ('loz', 'Lozi'), ('lub', 'Luba-Katanga'), ('lua', 'Luba-Lulua'), ('lui', 'Luiseno'), ('smj', 'Lule Sami'), ('lun', 'Lunda'), ('luo', 'Luo (Kenya and Tanzania)'), ('lus', 'Lushai'), ('ltz', 'Luxembourgish; Letzeburgesch'), ('mkd', 'Macedonian'), ('mad', 'Madurese'), ('mag', 'Magahi'), ('mai', 'Maithili'), ('mak', 'Makasar'), ('mlg', 'Malagasy'), ('msa', 'Malay'), ('mal', 'Malayalam'), ('mlt', 'Maltese'), ('mnc', 'Manchu'), ('mdr', 'Mandar'), ('man', 'Mandingo'), ('mni', 'Manipuri'), ('mno', 'Manobo languages'), ('glv', 'Manx'), ('mri', 'Maori'), ('arn', 'Mapudungun; Mapuche'), ('mar', 'Marathi'), ('chm', 'Mari'), ('mah', 'Marshallese'), ('mwr', 'Marwari'), ('mas', 'Masai'), ('myn', 'Mayan languages'), ('men', 'Mende'), ('mic', "Mi'kmaq; Micmac"), ('min', 'Minangkabau'), ('mwl', 'Mirandese'), ('moh', 'Mohawk'), ('mdf', 'Moksha'), ('mol', 'Moldavian; Moldovan'), ('mkh', 'Mon-Khmer languages'), ('lol', 'Mongo'), ('mon', 'Mongolian'), ('mos', 'Mossi'), ('mul', 'Multiple languages'), ('mun', 'Munda languages'), ('nqo', "N'Ko"), ('nah', 'Nahuatl languages'), ('nau', 'Nauru'), ('nav', 'Navajo; Navaho'), ('nde', 'Ndebele, North; North Ndebele'), ('nbl', 'Ndebele, South; South Ndebele'), ('ndo', 'Ndonga'), ('nap', 'Neapolitan'), ('new', 'Nepal Bhasa; Newari'), ('nep', 'Nepali'), ('nia', 'Nias'), ('nic', 'Niger-Kordofanian languages'), ('ssa', 'Nilo-Saharan languages'), ('niu', 'Niuean'), ('zxx', 'No linguistic content; Not applicable'), ('nog', 'Nogai'), ('non', 'Norse, Old'), ('nai', 'North American Indian languages'), ('frr', 'Northern Frisian'), ('sme', 'Northern Sami'), ('nor', 'Norwegian'), ('nno', 'Norwegian Nynorsk; Nynorsk, Norwegian'), ('nub', 'Nubian languages'), ('nym', 'Nyamwezi'), ('nyn', 'Nyankole'), ('nyo', 'Nyoro'), ('nzi', 'Nzima'), ('oci', 'Occitan (post 1500)'), ('arc', 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), ('oji', 'Ojibwa'), ('ori', 'Oriya'), ('orm', 'Oromo'), ('osa', 'Osage'), ('oss', 'Ossetian; Ossetic'), ('oto', 'Otomian languages'), ('pal', 'Pahlavi'), ('pau', 'Palauan'), ('pli', 'Pali'), ('pam', 'Pampanga; Kapampangan'), ('pag', 'Pangasinan'), ('pan', 'Panjabi; Punjabi'), ('pap', 'Papiamento'), ('paa', 'Papuan languages'), ('nso', 'Pedi; Sepedi; Northern Sotho'), ('fas', 'Persian'), ('peo', 'Persian, Old (ca. 600-400 B.C.)'), ('phi', 'Philippine languages'), ('phn', 'Phoenician'), ('pon', 'Pohnpeian'), ('pol', 'Polish'), ('por', 'Portuguese'), ('pra', 'Prakrit languages'), ('pro', 'Provençal, Old (to 1500); Occitan, Old (to 1500)'), ('pus', 'Pushto; Pashto'), ('que', 'Quechua'), ('raj', 'Rajasthani'), ('rap', 'Rapanui'), ('rar', 'Rarotongan; Cook Islands Maori'), ('qaa-qtz', 'Reserved for local use'), ('roa', 'Romance languages'), ('ron', 'Romanian'), ('roh', 'Romansh'), ('rom', 'Romany'), ('run', 'Rundi'), ('rus', 'Russian'), ('sal', 'Salishan languages'), ('sam', 'Samaritan Aramaic'), ('smi', 'Sami languages'), ('smo', 'Samoan'), ('sad', 'Sandawe'), ('sag', 'Sango'), ('san', 'Sanskrit'), ('sat', 'Santali'), ('srd', 'Sardinian'), ('sas', 'Sasak'), ('sco', 'Scots'), ('sel', 'Selkup'), ('sem', 'Semitic languages'), ('srp', 'Serbian'), ('srr', 'Serer'), ('shn', 'Shan'), ('sna', 'Shona'), ('iii', 'Sichuan Yi; Nuosu'), ('scn', 'Sicilian'), ('sid', 'Sidamo'), ('sgn', 'Sign Languages'), ('bla', 'Siksika'), ('snd', 'Sindhi'), ('sin', 'Sinhala; Sinhalese'), ('sit', 'Sino-Tibetan languages'), ('sio', 'Siouan languages'), ('sms', 'Skolt Sami'), ('den', 'Slave (Athapascan)'), ('sla', 'Slavic languages'), ('slk', 'Slovak'), ('slv', 'Slovenian'), ('sog', 'Sogdian'), ('som', 'Somali'), ('son', 'Songhai languages'), ('snk', 'Soninke'), ('wen', 'Sorbian languages'), ('sot', 'Sotho, Southern'), ('sai', 'South American Indian languages'), ('alt', 'Southern Altai'), ('sma', 'Southern Sami'), ('spa', 'Spanish; Castilian'), ('srn', 'Sranan Tongo'), ('zgh', 'Standard Moroccan Tamazight'), ('suk', 'Sukuma'), ('sux', 'Sumerian'), ('sun', 'Sundanese'), ('sus', 'Susu'), ('swa', 'Swahili'), ('ssw', 'Swati'), ('swe', 'Swedish'), ('gsw', 'Swiss German; Alemannic; Alsatian'), ('syr', 'Syriac'), ('tgl', 'Tagalog'), ('tah', 'Tahitian'), ('tai', 'Tai languages'), ('tgk', 'Tajik'), ('tmh', 'Tamashek'), ('tam', 'Tamil'), ('tat', 'Tatar'), ('tel', 'Telugu'), ('ter', 'Tereno'), ('tet', 'Tetum'), ('tha', 'Thai'), ('bod', 'Tibetan'), ('tig', 'Tigre'), ('tir', 'Tigrinya'), ('tem', 'Timne'), ('tiv', 'Tiv'), ('tli', 'Tlingit'), ('tpi', 'Tok Pisin'), ('tkl', 'Tokelau'), ('tog', 'Tonga (Nyasa)'), ('ton', 'Tonga (Tonga Islands)'), ('tsi', 'Tsimshian'), ('tso', 'Tsonga'), ('tsn', 'Tswana'), ('tum', 'Tumbuka'), ('tup', 'Tupi languages'), ('tur', 'Turkish'), ('ota', 'Turkish, Ottoman (1500-1928)'), ('tuk', 'Turkmen'), ('tvl', 'Tuvalu'), ('tyv', 'Tuvinian'), ('twi', 'Twi'), ('udm', 'Udmurt'), ('uga', 'Ugaritic'), ('uig', 'Uighur; Uyghur'), ('ukr', 'Ukrainian'), ('umb', 'Umbundu'), ('mis', 'Uncoded languages'), ('und', 'Undetermined'), ('hsb', 'Upper Sorbian'), ('urd', 'Urdu'), ('uzb', 'Uzbek'), ('vai', 'Vai'), ('ven', 'Venda'), ('vie', 'Vietnamese'), ('vol', 'Volapük'), ('vot', 'Votic'), ('wak', 'Wakashan languages'), ('wln', 'Walloon'), ('war', 'Waray'), ('was', 'Washo'), ('cym', 'Welsh'), ('fry', 'Western Frisian'), ('wal', 'Wolaitta; Wolaytta'), ('wol', 'Wolof'), ('xho', 'Xhosa'), ('sah', 'Yakut'), ('yao', 'Yao'), ('yap', 'Yapese'), ('yid', 'Yiddish'), ('yor', 'Yoruba'), ('ypk', 'Yupik languages'), ('znd', 'Zande languages'), ('zap', 'Zapotec'), ('zza', 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), ('zen', 'Zenaga'), ('zha', 'Zhuang; Chuang'), ('zul', 'Zulu'), ('zun', 'Zuni')], max_length=200, null=True)), - ('is_remote', models.BooleanField(default=False, help_text='Check if this article is remote', verbose_name='Remote article')), - ('remote_url', models.URLField(blank=True, help_text='If the article is remote, its URL should be added.', null=True)), - ('competing_interests_bool', models.BooleanField(default=False)), - ('competing_interests', models.TextField(blank=True, null=True)), - ('date_started', models.DateTimeField(auto_now_add=True)), - ('date_accepted', models.DateTimeField(blank=True, null=True)), - ('date_declined', models.DateTimeField(blank=True, null=True)), - ('date_submitted', models.DateTimeField(blank=True, null=True)), - ('date_published', models.DateTimeField(blank=True, null=True)), - ('date_updated', models.DateTimeField(blank=True, null=True)), - ('current_step', models.IntegerField(default=1)), - ('page_numbers', models.CharField(blank=True, max_length=20, null=True)), - ('stage', models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned'), ('Under Review', 'Under Review'), ('Under Revision', 'Under Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published')], default='Unsubmitted', max_length=200)), - ('publication_fees', models.BooleanField(default=False)), - ('submission_requirements', models.BooleanField(default=False)), - ('copyright_notice', models.BooleanField(default=False)), - ('comments_editor', models.TextField(blank=True, null=True, verbose_name='Comments to the Editor')), - ('exclude_from_slider', models.BooleanField(default=False)), - ('peer_reviewed', models.BooleanField(default=True)), - ('is_import', models.BooleanField(default=False)), - ('article_agreement', models.TextField(default='')), - ('ithenticate_id', models.TextField(blank=True, null=True)), - ('ithenticate_score', models.IntegerField(blank=True, null=True)), - ('meta_image', models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/code/janeway/src/media'), upload_to=submission.models.article_media_upload)), - ('authors', models.ManyToManyField(blank=True, null=True, related_name='authors', to=settings.AUTH_USER_MODEL)), - ('correspondence_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='correspondence_author', to=settings.AUTH_USER_MODEL)), - ('data_figure_files', models.ManyToManyField(blank=True, null=True, related_name='data_figure_files', to='core.File')), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=300)), + ("subtitle", models.CharField(blank=True, max_length=300, null=True)), + ("abstract", models.TextField(blank=True)), + ( + "language", + models.CharField( + blank=True, + choices=[ + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokmål, Norwegian; Norwegian Bokmål"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ( + "nwc", + "Classical Newari; Old Newari; Classical Nepal Bhasa", + ), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ( + "ina", + "Interlingua (International Auxiliary Language Association)", + ), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ( + "arc", + "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)", + ), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Provençal, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volapük"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), + ], + max_length=200, + null=True, + ), + ), + ( + "is_remote", + models.BooleanField( + default=False, + help_text="Check if this article is remote", + verbose_name="Remote article", + ), + ), + ( + "remote_url", + models.URLField( + blank=True, + help_text="If the article is remote, its URL should be added.", + null=True, + ), + ), + ("competing_interests_bool", models.BooleanField(default=False)), + ("competing_interests", models.TextField(blank=True, null=True)), + ("date_started", models.DateTimeField(auto_now_add=True)), + ("date_accepted", models.DateTimeField(blank=True, null=True)), + ("date_declined", models.DateTimeField(blank=True, null=True)), + ("date_submitted", models.DateTimeField(blank=True, null=True)), + ("date_published", models.DateTimeField(blank=True, null=True)), + ("date_updated", models.DateTimeField(blank=True, null=True)), + ("current_step", models.IntegerField(default=1)), + ( + "page_numbers", + models.CharField(blank=True, max_length=20, null=True), + ), + ( + "stage", + models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned"), + ("Under Review", "Under Review"), + ("Under Revision", "Under Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ], + default="Unsubmitted", + max_length=200, + ), + ), + ("publication_fees", models.BooleanField(default=False)), + ("submission_requirements", models.BooleanField(default=False)), + ("copyright_notice", models.BooleanField(default=False)), + ( + "comments_editor", + models.TextField( + blank=True, null=True, verbose_name="Comments to the Editor" + ), + ), + ("exclude_from_slider", models.BooleanField(default=False)), + ("peer_reviewed", models.BooleanField(default=True)), + ("is_import", models.BooleanField(default=False)), + ("article_agreement", models.TextField(default="")), + ("ithenticate_id", models.TextField(blank=True, null=True)), + ("ithenticate_score", models.IntegerField(blank=True, null=True)), + ( + "meta_image", + models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/code/janeway/src/media" + ), + upload_to=submission.models.article_media_upload, + ), + ), + ( + "authors", + models.ManyToManyField( + blank=True, + null=True, + related_name="authors", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "correspondence_author", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="correspondence_author", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "data_figure_files", + models.ManyToManyField( + blank=True, + null=True, + related_name="data_figure_files", + to="core.File", + ), + ), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], options={ - 'ordering': ('-date_published', 'title'), + "ordering": ("-date_published", "title"), }, ), migrations.CreateModel( - name='ArticleStageLog', + name="ArticleStageLog", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stage_from', models.CharField(max_length=200)), - ('stage_to', models.CharField(max_length=200)), - ('date_time', models.DateTimeField(default=django.utils.timezone.now)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("stage_from", models.CharField(max_length=200)), + ("stage_to", models.CharField(max_length=200)), + ("date_time", models.DateTimeField(default=django.utils.timezone.now)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), ], options={ - 'ordering': ('-date_time',), + "ordering": ("-date_time",), }, ), migrations.CreateModel( - name='FrozenAuthor', + name="FrozenAuthor", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('first_name', models.CharField(blank=True, max_length=300, null=True)), - ('middle_name', models.CharField(blank=True, max_length=300, null=True)), - ('last_name', models.CharField(blank=True, max_length=300, null=True)), - ('institution', models.CharField(max_length=1000)), - ('department', models.CharField(blank=True, max_length=300, null=True)), - ('order', models.PositiveIntegerField(default=1)), - ('article', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('country', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Country')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("first_name", models.CharField(blank=True, max_length=300, null=True)), + ( + "middle_name", + models.CharField(blank=True, max_length=300, null=True), + ), + ("last_name", models.CharField(blank=True, max_length=300, null=True)), + ("institution", models.CharField(max_length=1000)), + ("department", models.CharField(blank=True, max_length=300, null=True)), + ("order", models.PositiveIntegerField(default=1)), + ( + "article", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "author", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "country", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="core.Country", + ), + ), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.CreateModel( - name='Keyword', + name="Keyword", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('word', models.CharField(max_length=200)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("word", models.CharField(max_length=200)), ], ), migrations.CreateModel( - name='Licence', + name="Licence", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=300)), - ('short_name', models.CharField(max_length=15)), - ('url', models.URLField(max_length=1000)), - ('text', models.TextField(blank=True, null=True)), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=300)), + ("short_name", models.CharField(max_length=15)), + ("url", models.URLField(max_length=1000)), + ("text", models.TextField(blank=True, null=True)), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), migrations.CreateModel( - name='Note', + name="Note", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField()), - ('date_time', models.DateTimeField(auto_now_add=True)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.TextField()), + ("date_time", models.DateTimeField(auto_now_add=True)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "creator", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('-date_time',), + "ordering": ("-date_time",), }, ), migrations.CreateModel( - name='PublisherNote', + name="PublisherNote", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField(max_length=4000)), - ('sequence', models.PositiveIntegerField(default=999)), - ('date_time', models.DateTimeField(auto_now_add=True)), - ('creator', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.TextField(max_length=4000)), + ("sequence", models.PositiveIntegerField(default=999)), + ("date_time", models.DateTimeField(auto_now_add=True)), + ( + "creator", + models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), migrations.CreateModel( - name='Section', + name="Section", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('number_of_reviewers', models.IntegerField(default=2)), - ('is_filterable', models.BooleanField(default=True)), - ('public_submissions', models.BooleanField(default=True)), - ('indexing', models.BooleanField(default=True)), - ('sequence', models.PositiveIntegerField(default=0)), - ('editors', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), - ('section_editors', models.ManyToManyField(related_name='section_editors', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("number_of_reviewers", models.IntegerField(default=2)), + ("is_filterable", models.BooleanField(default=True)), + ("public_submissions", models.BooleanField(default=True)), + ("indexing", models.BooleanField(default=True)), + ("sequence", models.PositiveIntegerField(default=0)), + ("editors", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), + ( + "section_editors", + models.ManyToManyField( + related_name="section_editors", to=settings.AUTH_USER_MODEL + ), + ), ], options={ - 'ordering': ('sequence',), + "ordering": ("sequence",), }, ), migrations.CreateModel( - name='SectionTranslation', + name="SectionTranslation", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('plural', models.CharField(blank=True, max_length=200, null=True)), - ('language_code', models.CharField(db_index=True, max_length=15)), - ('master', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='submission.Section')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ("plural", models.CharField(blank=True, max_length=200, null=True)), + ("language_code", models.CharField(db_index=True, max_length=15)), + ( + "master", + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="submission.Section", + ), + ), ], options={ - 'db_table': 'submission_section_translation', - 'db_tablespace': '', - 'managed': True, - 'abstract': False, - 'default_permissions': (), + "db_table": "submission_section_translation", + "db_tablespace": "", + "managed": True, + "abstract": False, + "default_permissions": (), }, ), migrations.AddField( - model_name='article', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, to='submission.Keyword'), + model_name="article", + name="keywords", + field=models.ManyToManyField( + blank=True, null=True, to="submission.Keyword" + ), ), migrations.AddField( - model_name='article', - name='large_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_file', to='core.File'), + model_name="article", + name="large_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="image_file", + to="core.File", + ), ), migrations.AddField( - model_name='article', - name='license', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Licence'), + model_name="article", + name="license", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Licence", + ), ), migrations.AddField( - model_name='article', - name='manuscript_files', - field=models.ManyToManyField(blank=True, null=True, related_name='manuscript_files', to='core.File'), + model_name="article", + name="manuscript_files", + field=models.ManyToManyField( + blank=True, null=True, related_name="manuscript_files", to="core.File" + ), ), migrations.AddField( - model_name='article', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="article", + name="owner", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='article', - name='primary_issue', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='journal.Issue'), + model_name="article", + name="primary_issue", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="journal.Issue", + ), ), migrations.AddField( - model_name='article', - name='publisher_notes', - field=models.ManyToManyField(blank=True, null=True, related_name='publisher_notes', to='submission.PublisherNote'), + model_name="article", + name="publisher_notes", + field=models.ManyToManyField( + blank=True, + null=True, + related_name="publisher_notes", + to="submission.PublisherNote", + ), ), migrations.AddField( - model_name='article', - name='render_galley', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='render_galley', to='core.Galley'), + model_name="article", + name="render_galley", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="render_galley", + to="core.Galley", + ), ), migrations.AddField( - model_name='article', - name='section', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Section'), + model_name="article", + name="section", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Section", + ), ), migrations.AddField( - model_name='article', - name='thumbnail_image_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='thumbnail_file', to='core.File'), + model_name="article", + name="thumbnail_image_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="thumbnail_file", + to="core.File", + ), ), migrations.AlterUniqueTogether( - name='sectiontranslation', - unique_together=set([('language_code', 'master')]), + name="sectiontranslation", + unique_together=set([("language_code", "master")]), ), ] diff --git a/src/submission/migrations/0002_auto_20170813_1302.py b/src/submission/migrations/0002_auto_20170813_1302.py index 45da9579d9..2c50182f2d 100755 --- a/src/submission/migrations/0002_auto_20170813_1302.py +++ b/src/submission/migrations/0002_auto_20170813_1302.py @@ -9,22 +9,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0001_initial'), + ("submission", "0001_initial"), ] operations = [ migrations.AlterModelManagers( - name='section', + name="section", managers=[ - ('objects', django.db.models.manager.Manager()), - ('_plain_manager', django.db.models.manager.Manager()), + ("objects", django.db.models.manager.Manager()), + ("_plain_manager", django.db.models.manager.Manager()), ], ), migrations.AlterField( - model_name='article', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/janeway/src/media'), upload_to=submission.models.article_media_upload), + model_name="article", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/Users/ajrbyers/Code/janeway/src/media" + ), + upload_to=submission.models.article_media_upload, + ), ), ] diff --git a/src/submission/migrations/0003_field.py b/src/submission/migrations/0003_field.py index f179cc1d66..13cb6e9d0a 100755 --- a/src/submission/migrations/0003_field.py +++ b/src/submission/migrations/0003_field.py @@ -6,22 +6,50 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0002_auto_20170813_1302'), + ("submission", "0002_auto_20170813_1302"), ] operations = [ migrations.CreateModel( - name='Field', + name="Field", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('kind', models.CharField(choices=[('text', 'Text Field'), ('textarea', 'Text Area'), ('check', 'Check Box'), ('select', 'Select'), ('email', 'Email'), ('date', 'Date')], max_length=50)), - ('choices', models.CharField(blank=True, help_text='Separate choices with the bar | character.', max_length=1000, null=True)), - ('required', models.BooleanField(default=True)), - ('order', models.IntegerField()), - ('help_text', models.TextField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ( + "kind", + models.CharField( + choices=[ + ("text", "Text Field"), + ("textarea", "Text Area"), + ("check", "Check Box"), + ("select", "Select"), + ("email", "Email"), + ("date", "Date"), + ], + max_length=50, + ), + ), + ( + "choices", + models.CharField( + blank=True, + help_text="Separate choices with the bar | character.", + max_length=1000, + null=True, + ), + ), + ("required", models.BooleanField(default=True)), + ("order", models.IntegerField()), + ("help_text", models.TextField()), ], ), ] diff --git a/src/submission/migrations/0004_fieldanswer.py b/src/submission/migrations/0004_fieldanswer.py index 68f8deff57..97e9734958 100755 --- a/src/submission/migrations/0004_fieldanswer.py +++ b/src/submission/migrations/0004_fieldanswer.py @@ -7,19 +7,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0003_field'), + ("submission", "0003_field"), ] operations = [ migrations.CreateModel( - name='FieldAnswer', + name="FieldAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField()), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article')), - ('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Field')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("answer", models.TextField()), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), + ), + ( + "field", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="submission.Field", + ), + ), ], ), ] diff --git a/src/submission/migrations/0005_field_width.py b/src/submission/migrations/0005_field_width.py index 405c26c070..8893054429 100755 --- a/src/submission/migrations/0005_field_width.py +++ b/src/submission/migrations/0005_field_width.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0004_fieldanswer'), + ("submission", "0004_fieldanswer"), ] operations = [ migrations.AddField( - model_name='field', - name='width', - field=models.CharField(choices=[('third', 'Third'), ('half', 'Half'), ('full,', 'Full')], default='full', max_length=50), + model_name="field", + name="width", + field=models.CharField( + choices=[("third", "Third"), ("half", "Half"), ("full,", "Full")], + default="full", + max_length=50, + ), ), ] diff --git a/src/submission/migrations/0006_field_journal.py b/src/submission/migrations/0006_field_journal.py index 515764c970..d515264f1b 100755 --- a/src/submission/migrations/0006_field_journal.py +++ b/src/submission/migrations/0006_field_journal.py @@ -7,16 +7,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0004_auto_20170813_1302'), - ('submission', '0005_field_width'), + ("journal", "0004_auto_20170813_1302"), + ("submission", "0005_field_width"), ] operations = [ migrations.AddField( - model_name='field', - name='journal', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="field", + name="journal", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), ] diff --git a/src/submission/migrations/0007_auto_20170822_1256.py b/src/submission/migrations/0007_auto_20170822_1256.py index e3e0048216..951de4fa0a 100755 --- a/src/submission/migrations/0007_auto_20170822_1256.py +++ b/src/submission/migrations/0007_auto_20170822_1256.py @@ -7,15 +7,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0006_field_journal'), + ("submission", "0006_field_journal"), ] operations = [ migrations.AlterField( - model_name='fieldanswer', - name='field', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Field'), + model_name="fieldanswer", + name="field", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Field", + ), ), ] diff --git a/src/submission/migrations/0008_auto_20170907_1159.py b/src/submission/migrations/0008_auto_20170907_1159.py index aae93376c7..df8f21da70 100755 --- a/src/submission/migrations/0008_auto_20170907_1159.py +++ b/src/submission/migrations/0008_auto_20170907_1159.py @@ -6,34 +6,541 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0007_auto_20170822_1256'), + ("submission", "0007_auto_20170822_1256"), ] operations = [ migrations.AlterModelOptions( - name='field', - options={'ordering': ('order', 'name')}, + name="field", + options={"ordering": ("order", "name")}, ), migrations.AddField( - model_name='licence', - name='available_for_submission', + model_name="licence", + name="available_for_submission", field=models.BooleanField(default=True), ), migrations.AlterField( - model_name='article', - name='language', - field=models.CharField(blank=True, choices=[('eng', 'English'), ('abk', 'Abkhazian'), ('ace', 'Achinese'), ('ach', 'Acoli'), ('ada', 'Adangme'), ('ady', 'Adyghe; Adygei'), ('aar', 'Afar'), ('afh', 'Afrihili'), ('afr', 'Afrikaans'), ('afa', 'Afro-Asiatic languages'), ('ain', 'Ainu'), ('aka', 'Akan'), ('akk', 'Akkadian'), ('sqi', 'Albanian'), ('ale', 'Aleut'), ('alg', 'Algonquian languages'), ('tut', 'Altaic languages'), ('amh', 'Amharic'), ('anp', 'Angika'), ('apa', 'Apache languages'), ('ara', 'Arabic'), ('arg', 'Aragonese'), ('arp', 'Arapaho'), ('arw', 'Arawak'), ('hye', 'Armenian'), ('rup', 'Aromanian; Arumanian; Macedo-Romanian'), ('art', 'Artificial languages'), ('asm', 'Assamese'), ('ast', 'Asturian; Bable; Leonese; Asturleonese'), ('ath', 'Athapascan languages'), ('aus', 'Australian languages'), ('map', 'Austronesian languages'), ('ava', 'Avaric'), ('ave', 'Avestan'), ('awa', 'Awadhi'), ('aym', 'Aymara'), ('aze', 'Azerbaijani'), ('ban', 'Balinese'), ('bat', 'Baltic languages'), ('bal', 'Baluchi'), ('bam', 'Bambara'), ('bai', 'Bamileke languages'), ('bad', 'Banda languages'), ('bnt', 'Bantu languages'), ('bas', 'Basa'), ('bak', 'Bashkir'), ('eus', 'Basque'), ('btk', 'Batak languages'), ('bej', 'Beja; Bedawiyet'), ('bel', 'Belarusian'), ('bem', 'Bemba'), ('ben', 'Bengali'), ('ber', 'Berber languages'), ('bho', 'Bhojpuri'), ('bih', 'Bihari languages'), ('bik', 'Bikol'), ('bin', 'Bini; Edo'), ('bis', 'Bislama'), ('byn', 'Blin; Bilin'), ('zbl', 'Blissymbols; Blissymbolics; Bliss'), ('nob', 'Bokmål, Norwegian; Norwegian Bokmål'), ('bos', 'Bosnian'), ('bra', 'Braj'), ('bre', 'Breton'), ('bug', 'Buginese'), ('bul', 'Bulgarian'), ('bua', 'Buriat'), ('mya', 'Burmese'), ('cad', 'Caddo'), ('cat', 'Catalan; Valencian'), ('cau', 'Caucasian languages'), ('ceb', 'Cebuano'), ('cel', 'Celtic languages'), ('cai', 'Central American Indian languages'), ('khm', 'Central Khmer'), ('chg', 'Chagatai'), ('cmc', 'Chamic languages'), ('cha', 'Chamorro'), ('che', 'Chechen'), ('chr', 'Cherokee'), ('chy', 'Cheyenne'), ('chb', 'Chibcha'), ('nya', 'Chichewa; Chewa; Nyanja'), ('zho', 'Chinese'), ('chn', 'Chinook jargon'), ('chp', 'Chipewyan; Dene Suline'), ('cho', 'Choctaw'), ('chu', 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), ('chk', 'Chuukese'), ('chv', 'Chuvash'), ('nwc', 'Classical Newari; Old Newari; Classical Nepal Bhasa'), ('syc', 'Classical Syriac'), ('cop', 'Coptic'), ('cor', 'Cornish'), ('cos', 'Corsican'), ('cre', 'Cree'), ('mus', 'Creek'), ('crp', 'Creoles and pidgins'), ('cpe', 'Creoles and pidgins, English based'), ('cpf', 'Creoles and pidgins, French-based'), ('cpp', 'Creoles and pidgins, Portuguese-based'), ('crh', 'Crimean Tatar; Crimean Turkish'), ('hrv', 'Croatian'), ('cus', 'Cushitic languages'), ('ces', 'Czech'), ('dak', 'Dakota'), ('dan', 'Danish'), ('dar', 'Dargwa'), ('del', 'Delaware'), ('din', 'Dinka'), ('div', 'Divehi; Dhivehi; Maldivian'), ('doi', 'Dogri'), ('dgr', 'Dogrib'), ('dra', 'Dravidian languages'), ('dua', 'Duala'), ('dum', 'Dutch, Middle (ca. 1050-1350)'), ('nld', 'Dutch; Flemish'), ('dyu', 'Dyula'), ('dzo', 'Dzongkha'), ('frs', 'Eastern Frisian'), ('efi', 'Efik'), ('egy', 'Egyptian (Ancient)'), ('eka', 'Ekajuk'), ('elx', 'Elamite'), ('enm', 'English, Middle (1100-1500)'), ('ang', 'English, Old (ca. 450-1100)'), ('myv', 'Erzya'), ('epo', 'Esperanto'), ('est', 'Estonian'), ('ewe', 'Ewe'), ('ewo', 'Ewondo'), ('fan', 'Fang'), ('fat', 'Fanti'), ('fao', 'Faroese'), ('fij', 'Fijian'), ('fil', 'Filipino; Pilipino'), ('fin', 'Finnish'), ('fiu', 'Finno-Ugrian languages'), ('fon', 'Fon'), ('fra', 'French'), ('frm', 'French, Middle (ca. 1400-1600)'), ('fro', 'French, Old (842-ca. 1400)'), ('fur', 'Friulian'), ('ful', 'Fulah'), ('gaa', 'Ga'), ('gla', 'Gaelic; Scottish Gaelic'), ('car', 'Galibi Carib'), ('glg', 'Galician'), ('lug', 'Ganda'), ('gay', 'Gayo'), ('gba', 'Gbaya'), ('gez', 'Geez'), ('kat', 'Georgian'), ('deu', 'German'), ('gmh', 'German, Middle High (ca. 1050-1500)'), ('goh', 'German, Old High (ca. 750-1050)'), ('gem', 'Germanic languages'), ('gil', 'Gilbertese'), ('gon', 'Gondi'), ('gor', 'Gorontalo'), ('got', 'Gothic'), ('grb', 'Grebo'), ('grc', 'Greek, Ancient (to 1453)'), ('ell', 'Greek, Modern (1453-)'), ('grn', 'Guarani'), ('guj', 'Gujarati'), ('gwi', "Gwich'in"), ('hai', 'Haida'), ('hat', 'Haitian; Haitian Creole'), ('hau', 'Hausa'), ('haw', 'Hawaiian'), ('heb', 'Hebrew'), ('her', 'Herero'), ('hil', 'Hiligaynon'), ('him', 'Himachali languages; Western Pahari languages'), ('hin', 'Hindi'), ('hmo', 'Hiri Motu'), ('hit', 'Hittite'), ('hmn', 'Hmong; Mong'), ('hun', 'Hungarian'), ('hup', 'Hupa'), ('iba', 'Iban'), ('isl', 'Icelandic'), ('ido', 'Ido'), ('ibo', 'Igbo'), ('ijo', 'Ijo languages'), ('ilo', 'Iloko'), ('smn', 'Inari Sami'), ('inc', 'Indic languages'), ('ine', 'Indo-European languages'), ('ind', 'Indonesian'), ('inh', 'Ingush'), ('ina', 'Interlingua (International Auxiliary Language Association)'), ('ile', 'Interlingue; Occidental'), ('iku', 'Inuktitut'), ('ipk', 'Inupiaq'), ('ira', 'Iranian languages'), ('gle', 'Irish'), ('mga', 'Irish, Middle (900-1200)'), ('sga', 'Irish, Old (to 900)'), ('iro', 'Iroquoian languages'), ('ita', 'Italian'), ('jpn', 'Japanese'), ('jav', 'Javanese'), ('jrb', 'Judeo-Arabic'), ('jpr', 'Judeo-Persian'), ('kbd', 'Kabardian'), ('kab', 'Kabyle'), ('kac', 'Kachin; Jingpho'), ('kal', 'Kalaallisut; Greenlandic'), ('xal', 'Kalmyk; Oirat'), ('kam', 'Kamba'), ('kan', 'Kannada'), ('kau', 'Kanuri'), ('kaa', 'Kara-Kalpak'), ('krc', 'Karachay-Balkar'), ('krl', 'Karelian'), ('kar', 'Karen languages'), ('kas', 'Kashmiri'), ('csb', 'Kashubian'), ('kaw', 'Kawi'), ('kaz', 'Kazakh'), ('kha', 'Khasi'), ('khi', 'Khoisan languages'), ('kho', 'Khotanese;Sakan'), ('kik', 'Kikuyu; Gikuyu'), ('kmb', 'Kimbundu'), ('kin', 'Kinyarwanda'), ('kir', 'Kirghiz; Kyrgyz'), ('tlh', 'Klingon; tlhIngan-Hol'), ('kom', 'Komi'), ('kon', 'Kongo'), ('kok', 'Konkani'), ('kor', 'Korean'), ('kos', 'Kosraean'), ('kpe', 'Kpelle'), ('kro', 'Kru languages'), ('kua', 'Kuanyama; Kwanyama'), ('kum', 'Kumyk'), ('kur', 'Kurdish'), ('kru', 'Kurukh'), ('kut', 'Kutenai'), ('lad', 'Ladino'), ('lah', 'Lahnda'), ('lam', 'Lamba'), ('day', 'Land Dayak languages'), ('lao', 'Lao'), ('lat', 'Latin'), ('lav', 'Latvian'), ('lez', 'Lezghian'), ('lim', 'Limburgan; Limburger; Limburgish'), ('lin', 'Lingala'), ('lit', 'Lithuanian'), ('jbo', 'Lojban'), ('nds', 'Low German; Low Saxon; German, Low; Saxon, Low'), ('dsb', 'Lower Sorbian'), ('loz', 'Lozi'), ('lub', 'Luba-Katanga'), ('lua', 'Luba-Lulua'), ('lui', 'Luiseno'), ('smj', 'Lule Sami'), ('lun', 'Lunda'), ('luo', 'Luo (Kenya and Tanzania)'), ('lus', 'Lushai'), ('ltz', 'Luxembourgish; Letzeburgesch'), ('mkd', 'Macedonian'), ('mad', 'Madurese'), ('mag', 'Magahi'), ('mai', 'Maithili'), ('mak', 'Makasar'), ('mlg', 'Malagasy'), ('msa', 'Malay'), ('mal', 'Malayalam'), ('mlt', 'Maltese'), ('mnc', 'Manchu'), ('mdr', 'Mandar'), ('man', 'Mandingo'), ('mni', 'Manipuri'), ('mno', 'Manobo languages'), ('glv', 'Manx'), ('mri', 'Maori'), ('arn', 'Mapudungun; Mapuche'), ('mar', 'Marathi'), ('chm', 'Mari'), ('mah', 'Marshallese'), ('mwr', 'Marwari'), ('mas', 'Masai'), ('myn', 'Mayan languages'), ('men', 'Mende'), ('mic', "Mi'kmaq; Micmac"), ('min', 'Minangkabau'), ('mwl', 'Mirandese'), ('moh', 'Mohawk'), ('mdf', 'Moksha'), ('mol', 'Moldavian; Moldovan'), ('mkh', 'Mon-Khmer languages'), ('lol', 'Mongo'), ('mon', 'Mongolian'), ('mos', 'Mossi'), ('mul', 'Multiple languages'), ('mun', 'Munda languages'), ('nqo', "N'Ko"), ('nah', 'Nahuatl languages'), ('nau', 'Nauru'), ('nav', 'Navajo; Navaho'), ('nde', 'Ndebele, North; North Ndebele'), ('nbl', 'Ndebele, South; South Ndebele'), ('ndo', 'Ndonga'), ('nap', 'Neapolitan'), ('new', 'Nepal Bhasa; Newari'), ('nep', 'Nepali'), ('nia', 'Nias'), ('nic', 'Niger-Kordofanian languages'), ('ssa', 'Nilo-Saharan languages'), ('niu', 'Niuean'), ('zxx', 'No linguistic content; Not applicable'), ('nog', 'Nogai'), ('non', 'Norse, Old'), ('nai', 'North American Indian languages'), ('frr', 'Northern Frisian'), ('sme', 'Northern Sami'), ('nor', 'Norwegian'), ('nno', 'Norwegian Nynorsk; Nynorsk, Norwegian'), ('nub', 'Nubian languages'), ('nym', 'Nyamwezi'), ('nyn', 'Nyankole'), ('nyo', 'Nyoro'), ('nzi', 'Nzima'), ('oci', 'Occitan (post 1500)'), ('arc', 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), ('oji', 'Ojibwa'), ('ori', 'Oriya'), ('orm', 'Oromo'), ('osa', 'Osage'), ('oss', 'Ossetian; Ossetic'), ('oto', 'Otomian languages'), ('pal', 'Pahlavi'), ('pau', 'Palauan'), ('pli', 'Pali'), ('pam', 'Pampanga; Kapampangan'), ('pag', 'Pangasinan'), ('pan', 'Panjabi; Punjabi'), ('pap', 'Papiamento'), ('paa', 'Papuan languages'), ('nso', 'Pedi; Sepedi; Northern Sotho'), ('fas', 'Persian'), ('peo', 'Persian, Old (ca. 600-400 B.C.)'), ('phi', 'Philippine languages'), ('phn', 'Phoenician'), ('pon', 'Pohnpeian'), ('pol', 'Polish'), ('por', 'Portuguese'), ('pra', 'Prakrit languages'), ('pro', 'Provençal, Old (to 1500); Occitan, Old (to 1500)'), ('pus', 'Pushto; Pashto'), ('que', 'Quechua'), ('raj', 'Rajasthani'), ('rap', 'Rapanui'), ('rar', 'Rarotongan; Cook Islands Maori'), ('qaa-qtz', 'Reserved for local use'), ('roa', 'Romance languages'), ('ron', 'Romanian'), ('roh', 'Romansh'), ('rom', 'Romany'), ('run', 'Rundi'), ('rus', 'Russian'), ('sal', 'Salishan languages'), ('sam', 'Samaritan Aramaic'), ('smi', 'Sami languages'), ('smo', 'Samoan'), ('sad', 'Sandawe'), ('sag', 'Sango'), ('san', 'Sanskrit'), ('sat', 'Santali'), ('srd', 'Sardinian'), ('sas', 'Sasak'), ('sco', 'Scots'), ('sel', 'Selkup'), ('sem', 'Semitic languages'), ('srp', 'Serbian'), ('srr', 'Serer'), ('shn', 'Shan'), ('sna', 'Shona'), ('iii', 'Sichuan Yi; Nuosu'), ('scn', 'Sicilian'), ('sid', 'Sidamo'), ('sgn', 'Sign Languages'), ('bla', 'Siksika'), ('snd', 'Sindhi'), ('sin', 'Sinhala; Sinhalese'), ('sit', 'Sino-Tibetan languages'), ('sio', 'Siouan languages'), ('sms', 'Skolt Sami'), ('den', 'Slave (Athapascan)'), ('sla', 'Slavic languages'), ('slk', 'Slovak'), ('slv', 'Slovenian'), ('sog', 'Sogdian'), ('som', 'Somali'), ('son', 'Songhai languages'), ('snk', 'Soninke'), ('wen', 'Sorbian languages'), ('sot', 'Sotho, Southern'), ('sai', 'South American Indian languages'), ('alt', 'Southern Altai'), ('sma', 'Southern Sami'), ('spa', 'Spanish; Castilian'), ('srn', 'Sranan Tongo'), ('zgh', 'Standard Moroccan Tamazight'), ('suk', 'Sukuma'), ('sux', 'Sumerian'), ('sun', 'Sundanese'), ('sus', 'Susu'), ('swa', 'Swahili'), ('ssw', 'Swati'), ('swe', 'Swedish'), ('gsw', 'Swiss German; Alemannic; Alsatian'), ('syr', 'Syriac'), ('tgl', 'Tagalog'), ('tah', 'Tahitian'), ('tai', 'Tai languages'), ('tgk', 'Tajik'), ('tmh', 'Tamashek'), ('tam', 'Tamil'), ('tat', 'Tatar'), ('tel', 'Telugu'), ('ter', 'Tereno'), ('tet', 'Tetum'), ('tha', 'Thai'), ('bod', 'Tibetan'), ('tig', 'Tigre'), ('tir', 'Tigrinya'), ('tem', 'Timne'), ('tiv', 'Tiv'), ('tli', 'Tlingit'), ('tpi', 'Tok Pisin'), ('tkl', 'Tokelau'), ('tog', 'Tonga (Nyasa)'), ('ton', 'Tonga (Tonga Islands)'), ('tsi', 'Tsimshian'), ('tso', 'Tsonga'), ('tsn', 'Tswana'), ('tum', 'Tumbuka'), ('tup', 'Tupi languages'), ('tur', 'Turkish'), ('ota', 'Turkish, Ottoman (1500-1928)'), ('tuk', 'Turkmen'), ('tvl', 'Tuvalu'), ('tyv', 'Tuvinian'), ('twi', 'Twi'), ('udm', 'Udmurt'), ('uga', 'Ugaritic'), ('uig', 'Uighur; Uyghur'), ('ukr', 'Ukrainian'), ('umb', 'Umbundu'), ('mis', 'Uncoded languages'), ('und', 'Undetermined'), ('hsb', 'Upper Sorbian'), ('urd', 'Urdu'), ('uzb', 'Uzbek'), ('vai', 'Vai'), ('ven', 'Venda'), ('vie', 'Vietnamese'), ('vol', 'Volapük'), ('vot', 'Votic'), ('wak', 'Wakashan languages'), ('wln', 'Walloon'), ('war', 'Waray'), ('was', 'Washo'), ('cym', 'Welsh'), ('fry', 'Western Frisian'), ('wal', 'Wolaitta; Wolaytta'), ('wol', 'Wolof'), ('xho', 'Xhosa'), ('sah', 'Yakut'), ('yao', 'Yao'), ('yap', 'Yapese'), ('yid', 'Yiddish'), ('yor', 'Yoruba'), ('ypk', 'Yupik languages'), ('znd', 'Zande languages'), ('zap', 'Zapotec'), ('zza', 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), ('zen', 'Zenaga'), ('zha', 'Zhuang; Chuang'), ('zul', 'Zulu'), ('zun', 'Zuni')], help_text='The primary language of the article', max_length=200, null=True), + model_name="article", + name="language", + field=models.CharField( + blank=True, + choices=[ + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokmål, Norwegian; Norwegian Bokmål"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ("nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa"), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ( + "ina", + "Interlingua (International Auxiliary Language Association)", + ), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ( + "arc", + "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)", + ), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Provençal, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volapük"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), + ], + help_text="The primary language of the article", + max_length=200, + null=True, + ), ), migrations.AlterField( - model_name='article', - name='subtitle', - field=models.CharField(blank=True, help_text='Do not use--deprecated in version 1.4.1 and later.', max_length=300, null=True), + model_name="article", + name="subtitle", + field=models.CharField( + blank=True, + help_text="Do not use--deprecated in version 1.4.1 and later.", + max_length=300, + null=True, + ), ), migrations.AlterField( - model_name='article', - name='title', - field=models.CharField(help_text='Your article title', max_length=300), + model_name="article", + name="title", + field=models.CharField(help_text="Your article title", max_length=300), ), ] diff --git a/src/submission/migrations/0008_auto_20170907_1652.py b/src/submission/migrations/0008_auto_20170907_1652.py index 765191f9f1..f8547ec4b3 100755 --- a/src/submission/migrations/0008_auto_20170907_1652.py +++ b/src/submission/migrations/0008_auto_20170907_1652.py @@ -8,55 +8,580 @@ class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('submission', '0007_auto_20170822_1256'), + ("submission", "0007_auto_20170822_1256"), ] operations = [ migrations.CreateModel( - name='ArticleAuthorOrder', + name="ArticleAuthorOrder", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=0)), ], options={ - 'ordering': ('order',), + "ordering": ("order",), }, ), migrations.AlterModelOptions( - name='field', - options={'ordering': ('order', 'name')}, + name="field", + options={"ordering": ("order", "name")}, ), migrations.AlterField( - model_name='article', - name='language', - field=models.CharField(blank=True, choices=[('eng', 'English'), ('abk', 'Abkhazian'), ('ace', 'Achinese'), ('ach', 'Acoli'), ('ada', 'Adangme'), ('ady', 'Adyghe; Adygei'), ('aar', 'Afar'), ('afh', 'Afrihili'), ('afr', 'Afrikaans'), ('afa', 'Afro-Asiatic languages'), ('ain', 'Ainu'), ('aka', 'Akan'), ('akk', 'Akkadian'), ('sqi', 'Albanian'), ('ale', 'Aleut'), ('alg', 'Algonquian languages'), ('tut', 'Altaic languages'), ('amh', 'Amharic'), ('anp', 'Angika'), ('apa', 'Apache languages'), ('ara', 'Arabic'), ('arg', 'Aragonese'), ('arp', 'Arapaho'), ('arw', 'Arawak'), ('hye', 'Armenian'), ('rup', 'Aromanian; Arumanian; Macedo-Romanian'), ('art', 'Artificial languages'), ('asm', 'Assamese'), ('ast', 'Asturian; Bable; Leonese; Asturleonese'), ('ath', 'Athapascan languages'), ('aus', 'Australian languages'), ('map', 'Austronesian languages'), ('ava', 'Avaric'), ('ave', 'Avestan'), ('awa', 'Awadhi'), ('aym', 'Aymara'), ('aze', 'Azerbaijani'), ('ban', 'Balinese'), ('bat', 'Baltic languages'), ('bal', 'Baluchi'), ('bam', 'Bambara'), ('bai', 'Bamileke languages'), ('bad', 'Banda languages'), ('bnt', 'Bantu languages'), ('bas', 'Basa'), ('bak', 'Bashkir'), ('eus', 'Basque'), ('btk', 'Batak languages'), ('bej', 'Beja; Bedawiyet'), ('bel', 'Belarusian'), ('bem', 'Bemba'), ('ben', 'Bengali'), ('ber', 'Berber languages'), ('bho', 'Bhojpuri'), ('bih', 'Bihari languages'), ('bik', 'Bikol'), ('bin', 'Bini; Edo'), ('bis', 'Bislama'), ('byn', 'Blin; Bilin'), ('zbl', 'Blissymbols; Blissymbolics; Bliss'), ('nob', 'Bokmål, Norwegian; Norwegian Bokmål'), ('bos', 'Bosnian'), ('bra', 'Braj'), ('bre', 'Breton'), ('bug', 'Buginese'), ('bul', 'Bulgarian'), ('bua', 'Buriat'), ('mya', 'Burmese'), ('cad', 'Caddo'), ('cat', 'Catalan; Valencian'), ('cau', 'Caucasian languages'), ('ceb', 'Cebuano'), ('cel', 'Celtic languages'), ('cai', 'Central American Indian languages'), ('khm', 'Central Khmer'), ('chg', 'Chagatai'), ('cmc', 'Chamic languages'), ('cha', 'Chamorro'), ('che', 'Chechen'), ('chr', 'Cherokee'), ('chy', 'Cheyenne'), ('chb', 'Chibcha'), ('nya', 'Chichewa; Chewa; Nyanja'), ('zho', 'Chinese'), ('chn', 'Chinook jargon'), ('chp', 'Chipewyan; Dene Suline'), ('cho', 'Choctaw'), ('chu', 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), ('chk', 'Chuukese'), ('chv', 'Chuvash'), ('nwc', 'Classical Newari; Old Newari; Classical Nepal Bhasa'), ('syc', 'Classical Syriac'), ('cop', 'Coptic'), ('cor', 'Cornish'), ('cos', 'Corsican'), ('cre', 'Cree'), ('mus', 'Creek'), ('crp', 'Creoles and pidgins'), ('cpe', 'Creoles and pidgins, English based'), ('cpf', 'Creoles and pidgins, French-based'), ('cpp', 'Creoles and pidgins, Portuguese-based'), ('crh', 'Crimean Tatar; Crimean Turkish'), ('hrv', 'Croatian'), ('cus', 'Cushitic languages'), ('ces', 'Czech'), ('dak', 'Dakota'), ('dan', 'Danish'), ('dar', 'Dargwa'), ('del', 'Delaware'), ('din', 'Dinka'), ('div', 'Divehi; Dhivehi; Maldivian'), ('doi', 'Dogri'), ('dgr', 'Dogrib'), ('dra', 'Dravidian languages'), ('dua', 'Duala'), ('dum', 'Dutch, Middle (ca. 1050-1350)'), ('nld', 'Dutch; Flemish'), ('dyu', 'Dyula'), ('dzo', 'Dzongkha'), ('frs', 'Eastern Frisian'), ('efi', 'Efik'), ('egy', 'Egyptian (Ancient)'), ('eka', 'Ekajuk'), ('elx', 'Elamite'), ('enm', 'English, Middle (1100-1500)'), ('ang', 'English, Old (ca. 450-1100)'), ('myv', 'Erzya'), ('epo', 'Esperanto'), ('est', 'Estonian'), ('ewe', 'Ewe'), ('ewo', 'Ewondo'), ('fan', 'Fang'), ('fat', 'Fanti'), ('fao', 'Faroese'), ('fij', 'Fijian'), ('fil', 'Filipino; Pilipino'), ('fin', 'Finnish'), ('fiu', 'Finno-Ugrian languages'), ('fon', 'Fon'), ('fra', 'French'), ('frm', 'French, Middle (ca. 1400-1600)'), ('fro', 'French, Old (842-ca. 1400)'), ('fur', 'Friulian'), ('ful', 'Fulah'), ('gaa', 'Ga'), ('gla', 'Gaelic; Scottish Gaelic'), ('car', 'Galibi Carib'), ('glg', 'Galician'), ('lug', 'Ganda'), ('gay', 'Gayo'), ('gba', 'Gbaya'), ('gez', 'Geez'), ('kat', 'Georgian'), ('deu', 'German'), ('gmh', 'German, Middle High (ca. 1050-1500)'), ('goh', 'German, Old High (ca. 750-1050)'), ('gem', 'Germanic languages'), ('gil', 'Gilbertese'), ('gon', 'Gondi'), ('gor', 'Gorontalo'), ('got', 'Gothic'), ('grb', 'Grebo'), ('grc', 'Greek, Ancient (to 1453)'), ('ell', 'Greek, Modern (1453-)'), ('grn', 'Guarani'), ('guj', 'Gujarati'), ('gwi', "Gwich'in"), ('hai', 'Haida'), ('hat', 'Haitian; Haitian Creole'), ('hau', 'Hausa'), ('haw', 'Hawaiian'), ('heb', 'Hebrew'), ('her', 'Herero'), ('hil', 'Hiligaynon'), ('him', 'Himachali languages; Western Pahari languages'), ('hin', 'Hindi'), ('hmo', 'Hiri Motu'), ('hit', 'Hittite'), ('hmn', 'Hmong; Mong'), ('hun', 'Hungarian'), ('hup', 'Hupa'), ('iba', 'Iban'), ('isl', 'Icelandic'), ('ido', 'Ido'), ('ibo', 'Igbo'), ('ijo', 'Ijo languages'), ('ilo', 'Iloko'), ('smn', 'Inari Sami'), ('inc', 'Indic languages'), ('ine', 'Indo-European languages'), ('ind', 'Indonesian'), ('inh', 'Ingush'), ('ina', 'Interlingua (International Auxiliary Language Association)'), ('ile', 'Interlingue; Occidental'), ('iku', 'Inuktitut'), ('ipk', 'Inupiaq'), ('ira', 'Iranian languages'), ('gle', 'Irish'), ('mga', 'Irish, Middle (900-1200)'), ('sga', 'Irish, Old (to 900)'), ('iro', 'Iroquoian languages'), ('ita', 'Italian'), ('jpn', 'Japanese'), ('jav', 'Javanese'), ('jrb', 'Judeo-Arabic'), ('jpr', 'Judeo-Persian'), ('kbd', 'Kabardian'), ('kab', 'Kabyle'), ('kac', 'Kachin; Jingpho'), ('kal', 'Kalaallisut; Greenlandic'), ('xal', 'Kalmyk; Oirat'), ('kam', 'Kamba'), ('kan', 'Kannada'), ('kau', 'Kanuri'), ('kaa', 'Kara-Kalpak'), ('krc', 'Karachay-Balkar'), ('krl', 'Karelian'), ('kar', 'Karen languages'), ('kas', 'Kashmiri'), ('csb', 'Kashubian'), ('kaw', 'Kawi'), ('kaz', 'Kazakh'), ('kha', 'Khasi'), ('khi', 'Khoisan languages'), ('kho', 'Khotanese;Sakan'), ('kik', 'Kikuyu; Gikuyu'), ('kmb', 'Kimbundu'), ('kin', 'Kinyarwanda'), ('kir', 'Kirghiz; Kyrgyz'), ('tlh', 'Klingon; tlhIngan-Hol'), ('kom', 'Komi'), ('kon', 'Kongo'), ('kok', 'Konkani'), ('kor', 'Korean'), ('kos', 'Kosraean'), ('kpe', 'Kpelle'), ('kro', 'Kru languages'), ('kua', 'Kuanyama; Kwanyama'), ('kum', 'Kumyk'), ('kur', 'Kurdish'), ('kru', 'Kurukh'), ('kut', 'Kutenai'), ('lad', 'Ladino'), ('lah', 'Lahnda'), ('lam', 'Lamba'), ('day', 'Land Dayak languages'), ('lao', 'Lao'), ('lat', 'Latin'), ('lav', 'Latvian'), ('lez', 'Lezghian'), ('lim', 'Limburgan; Limburger; Limburgish'), ('lin', 'Lingala'), ('lit', 'Lithuanian'), ('jbo', 'Lojban'), ('nds', 'Low German; Low Saxon; German, Low; Saxon, Low'), ('dsb', 'Lower Sorbian'), ('loz', 'Lozi'), ('lub', 'Luba-Katanga'), ('lua', 'Luba-Lulua'), ('lui', 'Luiseno'), ('smj', 'Lule Sami'), ('lun', 'Lunda'), ('luo', 'Luo (Kenya and Tanzania)'), ('lus', 'Lushai'), ('ltz', 'Luxembourgish; Letzeburgesch'), ('mkd', 'Macedonian'), ('mad', 'Madurese'), ('mag', 'Magahi'), ('mai', 'Maithili'), ('mak', 'Makasar'), ('mlg', 'Malagasy'), ('msa', 'Malay'), ('mal', 'Malayalam'), ('mlt', 'Maltese'), ('mnc', 'Manchu'), ('mdr', 'Mandar'), ('man', 'Mandingo'), ('mni', 'Manipuri'), ('mno', 'Manobo languages'), ('glv', 'Manx'), ('mri', 'Maori'), ('arn', 'Mapudungun; Mapuche'), ('mar', 'Marathi'), ('chm', 'Mari'), ('mah', 'Marshallese'), ('mwr', 'Marwari'), ('mas', 'Masai'), ('myn', 'Mayan languages'), ('men', 'Mende'), ('mic', "Mi'kmaq; Micmac"), ('min', 'Minangkabau'), ('mwl', 'Mirandese'), ('moh', 'Mohawk'), ('mdf', 'Moksha'), ('mol', 'Moldavian; Moldovan'), ('mkh', 'Mon-Khmer languages'), ('lol', 'Mongo'), ('mon', 'Mongolian'), ('mos', 'Mossi'), ('mul', 'Multiple languages'), ('mun', 'Munda languages'), ('nqo', "N'Ko"), ('nah', 'Nahuatl languages'), ('nau', 'Nauru'), ('nav', 'Navajo; Navaho'), ('nde', 'Ndebele, North; North Ndebele'), ('nbl', 'Ndebele, South; South Ndebele'), ('ndo', 'Ndonga'), ('nap', 'Neapolitan'), ('new', 'Nepal Bhasa; Newari'), ('nep', 'Nepali'), ('nia', 'Nias'), ('nic', 'Niger-Kordofanian languages'), ('ssa', 'Nilo-Saharan languages'), ('niu', 'Niuean'), ('zxx', 'No linguistic content; Not applicable'), ('nog', 'Nogai'), ('non', 'Norse, Old'), ('nai', 'North American Indian languages'), ('frr', 'Northern Frisian'), ('sme', 'Northern Sami'), ('nor', 'Norwegian'), ('nno', 'Norwegian Nynorsk; Nynorsk, Norwegian'), ('nub', 'Nubian languages'), ('nym', 'Nyamwezi'), ('nyn', 'Nyankole'), ('nyo', 'Nyoro'), ('nzi', 'Nzima'), ('oci', 'Occitan (post 1500)'), ('arc', 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), ('oji', 'Ojibwa'), ('ori', 'Oriya'), ('orm', 'Oromo'), ('osa', 'Osage'), ('oss', 'Ossetian; Ossetic'), ('oto', 'Otomian languages'), ('pal', 'Pahlavi'), ('pau', 'Palauan'), ('pli', 'Pali'), ('pam', 'Pampanga; Kapampangan'), ('pag', 'Pangasinan'), ('pan', 'Panjabi; Punjabi'), ('pap', 'Papiamento'), ('paa', 'Papuan languages'), ('nso', 'Pedi; Sepedi; Northern Sotho'), ('fas', 'Persian'), ('peo', 'Persian, Old (ca. 600-400 B.C.)'), ('phi', 'Philippine languages'), ('phn', 'Phoenician'), ('pon', 'Pohnpeian'), ('pol', 'Polish'), ('por', 'Portuguese'), ('pra', 'Prakrit languages'), ('pro', 'Provençal, Old (to 1500); Occitan, Old (to 1500)'), ('pus', 'Pushto; Pashto'), ('que', 'Quechua'), ('raj', 'Rajasthani'), ('rap', 'Rapanui'), ('rar', 'Rarotongan; Cook Islands Maori'), ('qaa-qtz', 'Reserved for local use'), ('roa', 'Romance languages'), ('ron', 'Romanian'), ('roh', 'Romansh'), ('rom', 'Romany'), ('run', 'Rundi'), ('rus', 'Russian'), ('sal', 'Salishan languages'), ('sam', 'Samaritan Aramaic'), ('smi', 'Sami languages'), ('smo', 'Samoan'), ('sad', 'Sandawe'), ('sag', 'Sango'), ('san', 'Sanskrit'), ('sat', 'Santali'), ('srd', 'Sardinian'), ('sas', 'Sasak'), ('sco', 'Scots'), ('sel', 'Selkup'), ('sem', 'Semitic languages'), ('srp', 'Serbian'), ('srr', 'Serer'), ('shn', 'Shan'), ('sna', 'Shona'), ('iii', 'Sichuan Yi; Nuosu'), ('scn', 'Sicilian'), ('sid', 'Sidamo'), ('sgn', 'Sign Languages'), ('bla', 'Siksika'), ('snd', 'Sindhi'), ('sin', 'Sinhala; Sinhalese'), ('sit', 'Sino-Tibetan languages'), ('sio', 'Siouan languages'), ('sms', 'Skolt Sami'), ('den', 'Slave (Athapascan)'), ('sla', 'Slavic languages'), ('slk', 'Slovak'), ('slv', 'Slovenian'), ('sog', 'Sogdian'), ('som', 'Somali'), ('son', 'Songhai languages'), ('snk', 'Soninke'), ('wen', 'Sorbian languages'), ('sot', 'Sotho, Southern'), ('sai', 'South American Indian languages'), ('alt', 'Southern Altai'), ('sma', 'Southern Sami'), ('spa', 'Spanish; Castilian'), ('srn', 'Sranan Tongo'), ('zgh', 'Standard Moroccan Tamazight'), ('suk', 'Sukuma'), ('sux', 'Sumerian'), ('sun', 'Sundanese'), ('sus', 'Susu'), ('swa', 'Swahili'), ('ssw', 'Swati'), ('swe', 'Swedish'), ('gsw', 'Swiss German; Alemannic; Alsatian'), ('syr', 'Syriac'), ('tgl', 'Tagalog'), ('tah', 'Tahitian'), ('tai', 'Tai languages'), ('tgk', 'Tajik'), ('tmh', 'Tamashek'), ('tam', 'Tamil'), ('tat', 'Tatar'), ('tel', 'Telugu'), ('ter', 'Tereno'), ('tet', 'Tetum'), ('tha', 'Thai'), ('bod', 'Tibetan'), ('tig', 'Tigre'), ('tir', 'Tigrinya'), ('tem', 'Timne'), ('tiv', 'Tiv'), ('tli', 'Tlingit'), ('tpi', 'Tok Pisin'), ('tkl', 'Tokelau'), ('tog', 'Tonga (Nyasa)'), ('ton', 'Tonga (Tonga Islands)'), ('tsi', 'Tsimshian'), ('tso', 'Tsonga'), ('tsn', 'Tswana'), ('tum', 'Tumbuka'), ('tup', 'Tupi languages'), ('tur', 'Turkish'), ('ota', 'Turkish, Ottoman (1500-1928)'), ('tuk', 'Turkmen'), ('tvl', 'Tuvalu'), ('tyv', 'Tuvinian'), ('twi', 'Twi'), ('udm', 'Udmurt'), ('uga', 'Ugaritic'), ('uig', 'Uighur; Uyghur'), ('ukr', 'Ukrainian'), ('umb', 'Umbundu'), ('mis', 'Uncoded languages'), ('und', 'Undetermined'), ('hsb', 'Upper Sorbian'), ('urd', 'Urdu'), ('uzb', 'Uzbek'), ('vai', 'Vai'), ('ven', 'Venda'), ('vie', 'Vietnamese'), ('vol', 'Volapük'), ('vot', 'Votic'), ('wak', 'Wakashan languages'), ('wln', 'Walloon'), ('war', 'Waray'), ('was', 'Washo'), ('cym', 'Welsh'), ('fry', 'Western Frisian'), ('wal', 'Wolaitta; Wolaytta'), ('wol', 'Wolof'), ('xho', 'Xhosa'), ('sah', 'Yakut'), ('yao', 'Yao'), ('yap', 'Yapese'), ('yid', 'Yiddish'), ('yor', 'Yoruba'), ('ypk', 'Yupik languages'), ('znd', 'Zande languages'), ('zap', 'Zapotec'), ('zza', 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), ('zen', 'Zenaga'), ('zha', 'Zhuang; Chuang'), ('zul', 'Zulu'), ('zun', 'Zuni')], help_text='The primary language of the article', max_length=200, null=True), + model_name="article", + name="language", + field=models.CharField( + blank=True, + choices=[ + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokmål, Norwegian; Norwegian Bokmål"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ("nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa"), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ( + "ina", + "Interlingua (International Auxiliary Language Association)", + ), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ( + "arc", + "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)", + ), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Provençal, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volapük"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), + ], + help_text="The primary language of the article", + max_length=200, + null=True, + ), ), migrations.AlterField( - model_name='article', - name='license', - field=models.ForeignKey(blank=True, help_text='The license under which you wish to publish the article', null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Licence'), + model_name="article", + name="license", + field=models.ForeignKey( + blank=True, + help_text="The license under which you wish to publish the article", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Licence", + ), ), migrations.AlterField( - model_name='article', - name='subtitle', - field=models.CharField(blank=True, help_text='Do not use--deprecated in version 1.4.1 and later.', max_length=300, null=True), + model_name="article", + name="subtitle", + field=models.CharField( + blank=True, + help_text="Do not use--deprecated in version 1.4.1 and later.", + max_length=300, + null=True, + ), ), migrations.AlterField( - model_name='article', - name='title', - field=models.CharField(help_text='Your article title', max_length=300), + model_name="article", + name="title", + field=models.CharField(help_text="Your article title", max_length=300), ), migrations.AddField( - model_name='articleauthororder', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="articleauthororder", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='articleauthororder', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="articleauthororder", + name="author", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), ), ] diff --git a/src/submission/migrations/0009_merge_20170907_1721.py b/src/submission/migrations/0009_merge_20170907_1721.py index c0a345c203..223f5f5f88 100755 --- a/src/submission/migrations/0009_merge_20170907_1721.py +++ b/src/submission/migrations/0009_merge_20170907_1721.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0008_auto_20170907_1652'), - ('submission', '0008_auto_20170907_1159'), + ("submission", "0008_auto_20170907_1652"), + ("submission", "0008_auto_20170907_1159"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0010_auto_20170915_1146.py b/src/submission/migrations/0010_auto_20170915_1146.py index 3c6a0d3640..491fcf45be 100755 --- a/src/submission/migrations/0010_auto_20170915_1146.py +++ b/src/submission/migrations/0010_auto_20170915_1146.py @@ -7,20 +7,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0009_merge_20170907_1721'), + ("submission", "0009_merge_20170907_1721"), ] operations = [ migrations.AddField( - model_name='licence', - name='order', + model_name="licence", + name="order", field=models.PositiveIntegerField(default=0), ), migrations.AlterField( - model_name='article', - name='license', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.Licence'), + model_name="article", + name="license", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.Licence", + ), ), ] diff --git a/src/submission/migrations/0011_auto_20170921_0937.py b/src/submission/migrations/0011_auto_20170921_0937.py index 7ee1520359..e0514be1ee 100755 --- a/src/submission/migrations/0011_auto_20170921_0937.py +++ b/src/submission/migrations/0011_auto_20170921_0937.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0010_auto_20170915_1146'), + ("submission", "0010_auto_20170915_1146"), ] operations = [ migrations.AlterModelOptions( - name='licence', - options={'ordering': ('order', 'name')}, + name="licence", + options={"ordering": ("order", "name")}, ), ] diff --git a/src/submission/migrations/0012_auto_20170929_0835.py b/src/submission/migrations/0012_auto_20170929_0835.py index b9caaf4ecf..30a9f0226f 100755 --- a/src/submission/migrations/0012_auto_20170929_0835.py +++ b/src/submission/migrations/0012_auto_20170929_0835.py @@ -8,20 +8,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0011_auto_20170921_0937'), + ("submission", "0011_auto_20170921_0937"), ] operations = [ migrations.AddField( - model_name='article', - name='is_preprint', + model_name="article", + name="is_preprint", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='article', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/janeway/src/media'), upload_to=submission.models.article_media_upload), + model_name="article", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=django.core.files.storage.FileSystemStorage( + location="/home/ajrbyers/Code/janeway/src/media" + ), + upload_to=submission.models.article_media_upload, + ), ), ] diff --git a/src/submission/migrations/0013_auto_20171004_1002.py b/src/submission/migrations/0013_auto_20171004_1002.py index d5954a57a3..524315f345 100755 --- a/src/submission/migrations/0013_auto_20171004_1002.py +++ b/src/submission/migrations/0013_auto_20171004_1002.py @@ -7,15 +7,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0012_auto_20170929_0835'), + ("submission", "0012_auto_20170929_0835"), ] operations = [ migrations.AlterField( - model_name='article', - name='journal', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="article", + name="journal", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), ] diff --git a/src/submission/migrations/0014_auto_20171009_1028.py b/src/submission/migrations/0014_auto_20171009_1028.py index 51a6d03956..845d0fdc44 100755 --- a/src/submission/migrations/0014_auto_20171009_1028.py +++ b/src/submission/migrations/0014_auto_20171009_1028.py @@ -6,20 +6,40 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0013_auto_20171004_1002'), + ("submission", "0013_auto_20171004_1002"), ] operations = [ migrations.AddField( - model_name='article', - name='preprint_decision_notification', + model_name="article", + name="preprint_decision_notification", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='article', - name='stage', - field=models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned'), ('Under Review', 'Under Review'), ('Under Revision', 'Under Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published')], default='Unsubmitted', max_length=200), + model_name="article", + name="stage", + field=models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned"), + ("Under Review", "Under Review"), + ("Under Revision", "Under Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ], + default="Unsubmitted", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0015_auto_20171013_1104.py b/src/submission/migrations/0015_auto_20171013_1104.py index 227d061aa7..40498c8258 100755 --- a/src/submission/migrations/0015_auto_20171013_1104.py +++ b/src/submission/migrations/0015_auto_20171013_1104.py @@ -7,16 +7,15 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0014_auto_20171009_1028'), + ("submission", "0014_auto_20171009_1028"), ] operations = [ migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), + ("allarticles", django.db.models.manager.Manager()), ], ), ] diff --git a/src/submission/migrations/0016_auto_20171106_0909.py b/src/submission/migrations/0016_auto_20171106_0909.py index 319fae88b1..eb3df46329 100755 --- a/src/submission/migrations/0016_auto_20171106_0909.py +++ b/src/submission/migrations/0016_auto_20171106_0909.py @@ -7,21 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0012_presssetting_is_boolean'), - ('submission', '0015_auto_20171013_1104'), + ("press", "0012_presssetting_is_boolean"), + ("submission", "0015_auto_20171013_1104"), ] operations = [ migrations.AddField( - model_name='licence', - name='press', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='press.Press'), + model_name="licence", + name="press", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="press.Press", + ), ), migrations.AlterField( - model_name='licence', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='journal.Journal'), + model_name="licence", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="journal.Journal", + ), ), ] diff --git a/src/submission/migrations/0017_auto_20171114_1502.py b/src/submission/migrations/0017_auto_20171114_1502.py index 6b380e69e8..adcacf83e0 100755 --- a/src/submission/migrations/0017_auto_20171114_1502.py +++ b/src/submission/migrations/0017_auto_20171114_1502.py @@ -7,21 +7,30 @@ class Migration(migrations.Migration): - dependencies = [ - ('press', '0012_presssetting_is_boolean'), - ('submission', '0016_auto_20171106_0909'), + ("press", "0012_presssetting_is_boolean"), + ("submission", "0016_auto_20171106_0909"), ] operations = [ migrations.AddField( - model_name='field', - name='press', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='press.Press'), + model_name="field", + name="press", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="press.Press", + ), ), migrations.AlterField( - model_name='field', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="field", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), ] diff --git a/src/submission/migrations/0018_article_preprint_journal_article.py b/src/submission/migrations/0018_article_preprint_journal_article.py index 5ac09c96c2..d0a4ecab70 100644 --- a/src/submission/migrations/0018_article_preprint_journal_article.py +++ b/src/submission/migrations/0018_article_preprint_journal_article.py @@ -7,15 +7,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0017_auto_20171114_1502'), + ("submission", "0017_auto_20171114_1502"), ] operations = [ migrations.AddField( - model_name='article', - name='preprint_journal_article', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="article", + name="preprint_journal_article", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), ), ] diff --git a/src/submission/migrations/0019_auto_20171130_1115.py b/src/submission/migrations/0019_auto_20171130_1115.py index ad00b9b9b5..06f56e6f4f 100644 --- a/src/submission/migrations/0019_auto_20171130_1115.py +++ b/src/submission/migrations/0019_auto_20171130_1115.py @@ -7,20 +7,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0018_article_preprint_journal_article'), + ("submission", "0018_article_preprint_journal_article"), ] operations = [ migrations.AlterField( - model_name='article', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="article", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), ), migrations.AlterField( - model_name='article', - name='preprint_journal_article', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="article", + name="preprint_journal_article", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Article", + ), ), ] diff --git a/src/submission/migrations/0020_article_non_specialist_summary.py b/src/submission/migrations/0020_article_non_specialist_summary.py index 909d3b8ac5..714b884760 100644 --- a/src/submission/migrations/0020_article_non_specialist_summary.py +++ b/src/submission/migrations/0020_article_non_specialist_summary.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0019_auto_20171130_1115'), + ("submission", "0019_auto_20171130_1115"), ] operations = [ migrations.AddField( - model_name='article', - name='non_specialist_summary', - field=models.TextField(blank=True, help_text='A summary of the article for non specialists.', null=True), + model_name="article", + name="non_specialist_summary", + field=models.TextField( + blank=True, + help_text="A summary of the article for non specialists.", + null=True, + ), ), ] diff --git a/src/submission/migrations/0021_article_supplementary_files.py b/src/submission/migrations/0021_article_supplementary_files.py index 945b3c2a97..dda030e29f 100644 --- a/src/submission/migrations/0021_article_supplementary_files.py +++ b/src/submission/migrations/0021_article_supplementary_files.py @@ -6,16 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0015_supplementaryfile'), - ('submission', '0020_article_non_specialist_summary'), + ("core", "0015_supplementaryfile"), + ("submission", "0020_article_non_specialist_summary"), ] operations = [ migrations.AddField( - model_name='article', - name='supplementary_files', - field=models.ManyToManyField(blank=True, null=True, related_name='supp', to='core.SupplementaryFile'), + model_name="article", + name="supplementary_files", + field=models.ManyToManyField( + blank=True, null=True, related_name="supp", to="core.SupplementaryFile" + ), ), ] diff --git a/src/submission/migrations/0022_submissionconfiguration.py b/src/submission/migrations/0022_submissionconfiguration.py index 570f2a2a12..94ac0c6560 100644 --- a/src/submission/migrations/0022_submissionconfiguration.py +++ b/src/submission/migrations/0022_submissionconfiguration.py @@ -7,29 +7,42 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0010_auto_20180412_1544'), - ('submission', '0021_article_supplementary_files'), + ("journal", "0010_auto_20180412_1544"), + ("submission", "0021_article_supplementary_files"), ] operations = [ migrations.CreateModel( - name='SubmissionConfiguration', + name="SubmissionConfiguration", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('publication_fees', models.BooleanField(default=True)), - ('submission_check', models.BooleanField(default=True)), - ('copyright_notice', models.BooleanField(default=True)), - ('competing_interests', models.BooleanField(default=True)), - ('comments_to_the_editor', models.BooleanField(default=True)), - ('subtitle', models.BooleanField(default=True)), - ('abstract', models.BooleanField(default=True)), - ('language', models.BooleanField(default=True)), - ('license', models.BooleanField(default=True)), - ('keywords', models.BooleanField(default=True)), - ('figures_data', models.BooleanField(default=True)), - ('journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("publication_fees", models.BooleanField(default=True)), + ("submission_check", models.BooleanField(default=True)), + ("copyright_notice", models.BooleanField(default=True)), + ("competing_interests", models.BooleanField(default=True)), + ("comments_to_the_editor", models.BooleanField(default=True)), + ("subtitle", models.BooleanField(default=True)), + ("abstract", models.BooleanField(default=True)), + ("language", models.BooleanField(default=True)), + ("license", models.BooleanField(default=True)), + ("keywords", models.BooleanField(default=True)), + ("figures_data", models.BooleanField(default=True)), + ( + "journal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), ], ), ] diff --git a/src/submission/migrations/0023_auto_20180503_1033.py b/src/submission/migrations/0023_auto_20180503_1033.py index 4a9c1c5d51..982555f5b0 100644 --- a/src/submission/migrations/0023_auto_20180503_1033.py +++ b/src/submission/migrations/0023_auto_20180503_1033.py @@ -6,8 +6,8 @@ def create_submission_configuration(apps, schema_editor): - Journal = apps.get_model('journal', 'Journal') - Configuration = apps.get_model('submission', 'SubmissionConfiguration') + Journal = apps.get_model("journal", "Journal") + Configuration = apps.get_model("submission", "SubmissionConfiguration") journals = Journal.objects.all() @@ -16,11 +16,12 @@ def create_submission_configuration(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('submission', '0022_submissionconfiguration'), + ("submission", "0022_submissionconfiguration"), ] operations = [ - migrations.RunPython(create_submission_configuration, reverse_code=migrations.RunPython.noop) + migrations.RunPython( + create_submission_configuration, reverse_code=migrations.RunPython.noop + ) ] diff --git a/src/submission/migrations/0024_auto_20180503_1116.py b/src/submission/migrations/0024_auto_20180503_1116.py index e677395d91..10b91cef66 100644 --- a/src/submission/migrations/0024_auto_20180503_1116.py +++ b/src/submission/migrations/0024_auto_20180503_1116.py @@ -7,15 +7,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0023_auto_20180503_1033'), + ("submission", "0023_auto_20180503_1033"), ] operations = [ migrations.AlterField( - model_name='submissionconfiguration', - name='journal', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='journal.Journal'), + model_name="submissionconfiguration", + name="journal", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="journal.Journal" + ), ), ] diff --git a/src/submission/migrations/0025_auto_20180508_1138.py b/src/submission/migrations/0025_auto_20180508_1138.py index 89a0a129d8..dea9844696 100644 --- a/src/submission/migrations/0025_auto_20180508_1138.py +++ b/src/submission/migrations/0025_auto_20180508_1138.py @@ -7,35 +7,555 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0024_auto_20180503_1116'), + ("submission", "0024_auto_20180503_1116"), ] operations = [ migrations.AddField( - model_name='submissionconfiguration', - name='default_language', - field=models.CharField(choices=[('eng', 'English'), ('abk', 'Abkhazian'), ('ace', 'Achinese'), ('ach', 'Acoli'), ('ada', 'Adangme'), ('ady', 'Adyghe; Adygei'), ('aar', 'Afar'), ('afh', 'Afrihili'), ('afr', 'Afrikaans'), ('afa', 'Afro-Asiatic languages'), ('ain', 'Ainu'), ('aka', 'Akan'), ('akk', 'Akkadian'), ('sqi', 'Albanian'), ('ale', 'Aleut'), ('alg', 'Algonquian languages'), ('tut', 'Altaic languages'), ('amh', 'Amharic'), ('anp', 'Angika'), ('apa', 'Apache languages'), ('ara', 'Arabic'), ('arg', 'Aragonese'), ('arp', 'Arapaho'), ('arw', 'Arawak'), ('hye', 'Armenian'), ('rup', 'Aromanian; Arumanian; Macedo-Romanian'), ('art', 'Artificial languages'), ('asm', 'Assamese'), ('ast', 'Asturian; Bable; Leonese; Asturleonese'), ('ath', 'Athapascan languages'), ('aus', 'Australian languages'), ('map', 'Austronesian languages'), ('ava', 'Avaric'), ('ave', 'Avestan'), ('awa', 'Awadhi'), ('aym', 'Aymara'), ('aze', 'Azerbaijani'), ('ban', 'Balinese'), ('bat', 'Baltic languages'), ('bal', 'Baluchi'), ('bam', 'Bambara'), ('bai', 'Bamileke languages'), ('bad', 'Banda languages'), ('bnt', 'Bantu languages'), ('bas', 'Basa'), ('bak', 'Bashkir'), ('eus', 'Basque'), ('btk', 'Batak languages'), ('bej', 'Beja; Bedawiyet'), ('bel', 'Belarusian'), ('bem', 'Bemba'), ('ben', 'Bengali'), ('ber', 'Berber languages'), ('bho', 'Bhojpuri'), ('bih', 'Bihari languages'), ('bik', 'Bikol'), ('bin', 'Bini; Edo'), ('bis', 'Bislama'), ('byn', 'Blin; Bilin'), ('zbl', 'Blissymbols; Blissymbolics; Bliss'), ('nob', 'Bokmål, Norwegian; Norwegian Bokmål'), ('bos', 'Bosnian'), ('bra', 'Braj'), ('bre', 'Breton'), ('bug', 'Buginese'), ('bul', 'Bulgarian'), ('bua', 'Buriat'), ('mya', 'Burmese'), ('cad', 'Caddo'), ('cat', 'Catalan; Valencian'), ('cau', 'Caucasian languages'), ('ceb', 'Cebuano'), ('cel', 'Celtic languages'), ('cai', 'Central American Indian languages'), ('khm', 'Central Khmer'), ('chg', 'Chagatai'), ('cmc', 'Chamic languages'), ('cha', 'Chamorro'), ('che', 'Chechen'), ('chr', 'Cherokee'), ('chy', 'Cheyenne'), ('chb', 'Chibcha'), ('nya', 'Chichewa; Chewa; Nyanja'), ('zho', 'Chinese'), ('chn', 'Chinook jargon'), ('chp', 'Chipewyan; Dene Suline'), ('cho', 'Choctaw'), ('chu', 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), ('chk', 'Chuukese'), ('chv', 'Chuvash'), ('nwc', 'Classical Newari; Old Newari; Classical Nepal Bhasa'), ('syc', 'Classical Syriac'), ('cop', 'Coptic'), ('cor', 'Cornish'), ('cos', 'Corsican'), ('cre', 'Cree'), ('mus', 'Creek'), ('crp', 'Creoles and pidgins'), ('cpe', 'Creoles and pidgins, English based'), ('cpf', 'Creoles and pidgins, French-based'), ('cpp', 'Creoles and pidgins, Portuguese-based'), ('crh', 'Crimean Tatar; Crimean Turkish'), ('hrv', 'Croatian'), ('cus', 'Cushitic languages'), ('ces', 'Czech'), ('dak', 'Dakota'), ('dan', 'Danish'), ('dar', 'Dargwa'), ('del', 'Delaware'), ('din', 'Dinka'), ('div', 'Divehi; Dhivehi; Maldivian'), ('doi', 'Dogri'), ('dgr', 'Dogrib'), ('dra', 'Dravidian languages'), ('dua', 'Duala'), ('dum', 'Dutch, Middle (ca. 1050-1350)'), ('nld', 'Dutch; Flemish'), ('dyu', 'Dyula'), ('dzo', 'Dzongkha'), ('frs', 'Eastern Frisian'), ('efi', 'Efik'), ('egy', 'Egyptian (Ancient)'), ('eka', 'Ekajuk'), ('elx', 'Elamite'), ('enm', 'English, Middle (1100-1500)'), ('ang', 'English, Old (ca. 450-1100)'), ('myv', 'Erzya'), ('epo', 'Esperanto'), ('est', 'Estonian'), ('ewe', 'Ewe'), ('ewo', 'Ewondo'), ('fan', 'Fang'), ('fat', 'Fanti'), ('fao', 'Faroese'), ('fij', 'Fijian'), ('fil', 'Filipino; Pilipino'), ('fin', 'Finnish'), ('fiu', 'Finno-Ugrian languages'), ('fon', 'Fon'), ('fra', 'French'), ('frm', 'French, Middle (ca. 1400-1600)'), ('fro', 'French, Old (842-ca. 1400)'), ('fur', 'Friulian'), ('ful', 'Fulah'), ('gaa', 'Ga'), ('gla', 'Gaelic; Scottish Gaelic'), ('car', 'Galibi Carib'), ('glg', 'Galician'), ('lug', 'Ganda'), ('gay', 'Gayo'), ('gba', 'Gbaya'), ('gez', 'Geez'), ('kat', 'Georgian'), ('deu', 'German'), ('gmh', 'German, Middle High (ca. 1050-1500)'), ('goh', 'German, Old High (ca. 750-1050)'), ('gem', 'Germanic languages'), ('gil', 'Gilbertese'), ('gon', 'Gondi'), ('gor', 'Gorontalo'), ('got', 'Gothic'), ('grb', 'Grebo'), ('grc', 'Greek, Ancient (to 1453)'), ('ell', 'Greek, Modern (1453-)'), ('grn', 'Guarani'), ('guj', 'Gujarati'), ('gwi', "Gwich'in"), ('hai', 'Haida'), ('hat', 'Haitian; Haitian Creole'), ('hau', 'Hausa'), ('haw', 'Hawaiian'), ('heb', 'Hebrew'), ('her', 'Herero'), ('hil', 'Hiligaynon'), ('him', 'Himachali languages; Western Pahari languages'), ('hin', 'Hindi'), ('hmo', 'Hiri Motu'), ('hit', 'Hittite'), ('hmn', 'Hmong; Mong'), ('hun', 'Hungarian'), ('hup', 'Hupa'), ('iba', 'Iban'), ('isl', 'Icelandic'), ('ido', 'Ido'), ('ibo', 'Igbo'), ('ijo', 'Ijo languages'), ('ilo', 'Iloko'), ('smn', 'Inari Sami'), ('inc', 'Indic languages'), ('ine', 'Indo-European languages'), ('ind', 'Indonesian'), ('inh', 'Ingush'), ('ina', 'Interlingua (International Auxiliary Language Association)'), ('ile', 'Interlingue; Occidental'), ('iku', 'Inuktitut'), ('ipk', 'Inupiaq'), ('ira', 'Iranian languages'), ('gle', 'Irish'), ('mga', 'Irish, Middle (900-1200)'), ('sga', 'Irish, Old (to 900)'), ('iro', 'Iroquoian languages'), ('ita', 'Italian'), ('jpn', 'Japanese'), ('jav', 'Javanese'), ('jrb', 'Judeo-Arabic'), ('jpr', 'Judeo-Persian'), ('kbd', 'Kabardian'), ('kab', 'Kabyle'), ('kac', 'Kachin; Jingpho'), ('kal', 'Kalaallisut; Greenlandic'), ('xal', 'Kalmyk; Oirat'), ('kam', 'Kamba'), ('kan', 'Kannada'), ('kau', 'Kanuri'), ('kaa', 'Kara-Kalpak'), ('krc', 'Karachay-Balkar'), ('krl', 'Karelian'), ('kar', 'Karen languages'), ('kas', 'Kashmiri'), ('csb', 'Kashubian'), ('kaw', 'Kawi'), ('kaz', 'Kazakh'), ('kha', 'Khasi'), ('khi', 'Khoisan languages'), ('kho', 'Khotanese;Sakan'), ('kik', 'Kikuyu; Gikuyu'), ('kmb', 'Kimbundu'), ('kin', 'Kinyarwanda'), ('kir', 'Kirghiz; Kyrgyz'), ('tlh', 'Klingon; tlhIngan-Hol'), ('kom', 'Komi'), ('kon', 'Kongo'), ('kok', 'Konkani'), ('kor', 'Korean'), ('kos', 'Kosraean'), ('kpe', 'Kpelle'), ('kro', 'Kru languages'), ('kua', 'Kuanyama; Kwanyama'), ('kum', 'Kumyk'), ('kur', 'Kurdish'), ('kru', 'Kurukh'), ('kut', 'Kutenai'), ('lad', 'Ladino'), ('lah', 'Lahnda'), ('lam', 'Lamba'), ('day', 'Land Dayak languages'), ('lao', 'Lao'), ('lat', 'Latin'), ('lav', 'Latvian'), ('lez', 'Lezghian'), ('lim', 'Limburgan; Limburger; Limburgish'), ('lin', 'Lingala'), ('lit', 'Lithuanian'), ('jbo', 'Lojban'), ('nds', 'Low German; Low Saxon; German, Low; Saxon, Low'), ('dsb', 'Lower Sorbian'), ('loz', 'Lozi'), ('lub', 'Luba-Katanga'), ('lua', 'Luba-Lulua'), ('lui', 'Luiseno'), ('smj', 'Lule Sami'), ('lun', 'Lunda'), ('luo', 'Luo (Kenya and Tanzania)'), ('lus', 'Lushai'), ('ltz', 'Luxembourgish; Letzeburgesch'), ('mkd', 'Macedonian'), ('mad', 'Madurese'), ('mag', 'Magahi'), ('mai', 'Maithili'), ('mak', 'Makasar'), ('mlg', 'Malagasy'), ('msa', 'Malay'), ('mal', 'Malayalam'), ('mlt', 'Maltese'), ('mnc', 'Manchu'), ('mdr', 'Mandar'), ('man', 'Mandingo'), ('mni', 'Manipuri'), ('mno', 'Manobo languages'), ('glv', 'Manx'), ('mri', 'Maori'), ('arn', 'Mapudungun; Mapuche'), ('mar', 'Marathi'), ('chm', 'Mari'), ('mah', 'Marshallese'), ('mwr', 'Marwari'), ('mas', 'Masai'), ('myn', 'Mayan languages'), ('men', 'Mende'), ('mic', "Mi'kmaq; Micmac"), ('min', 'Minangkabau'), ('mwl', 'Mirandese'), ('moh', 'Mohawk'), ('mdf', 'Moksha'), ('mol', 'Moldavian; Moldovan'), ('mkh', 'Mon-Khmer languages'), ('lol', 'Mongo'), ('mon', 'Mongolian'), ('mos', 'Mossi'), ('mul', 'Multiple languages'), ('mun', 'Munda languages'), ('nqo', "N'Ko"), ('nah', 'Nahuatl languages'), ('nau', 'Nauru'), ('nav', 'Navajo; Navaho'), ('nde', 'Ndebele, North; North Ndebele'), ('nbl', 'Ndebele, South; South Ndebele'), ('ndo', 'Ndonga'), ('nap', 'Neapolitan'), ('new', 'Nepal Bhasa; Newari'), ('nep', 'Nepali'), ('nia', 'Nias'), ('nic', 'Niger-Kordofanian languages'), ('ssa', 'Nilo-Saharan languages'), ('niu', 'Niuean'), ('zxx', 'No linguistic content; Not applicable'), ('nog', 'Nogai'), ('non', 'Norse, Old'), ('nai', 'North American Indian languages'), ('frr', 'Northern Frisian'), ('sme', 'Northern Sami'), ('nor', 'Norwegian'), ('nno', 'Norwegian Nynorsk; Nynorsk, Norwegian'), ('nub', 'Nubian languages'), ('nym', 'Nyamwezi'), ('nyn', 'Nyankole'), ('nyo', 'Nyoro'), ('nzi', 'Nzima'), ('oci', 'Occitan (post 1500)'), ('arc', 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), ('oji', 'Ojibwa'), ('ori', 'Oriya'), ('orm', 'Oromo'), ('osa', 'Osage'), ('oss', 'Ossetian; Ossetic'), ('oto', 'Otomian languages'), ('pal', 'Pahlavi'), ('pau', 'Palauan'), ('pli', 'Pali'), ('pam', 'Pampanga; Kapampangan'), ('pag', 'Pangasinan'), ('pan', 'Panjabi; Punjabi'), ('pap', 'Papiamento'), ('paa', 'Papuan languages'), ('nso', 'Pedi; Sepedi; Northern Sotho'), ('fas', 'Persian'), ('peo', 'Persian, Old (ca. 600-400 B.C.)'), ('phi', 'Philippine languages'), ('phn', 'Phoenician'), ('pon', 'Pohnpeian'), ('pol', 'Polish'), ('por', 'Portuguese'), ('pra', 'Prakrit languages'), ('pro', 'Provençal, Old (to 1500); Occitan, Old (to 1500)'), ('pus', 'Pushto; Pashto'), ('que', 'Quechua'), ('raj', 'Rajasthani'), ('rap', 'Rapanui'), ('rar', 'Rarotongan; Cook Islands Maori'), ('qaa-qtz', 'Reserved for local use'), ('roa', 'Romance languages'), ('ron', 'Romanian'), ('roh', 'Romansh'), ('rom', 'Romany'), ('run', 'Rundi'), ('rus', 'Russian'), ('sal', 'Salishan languages'), ('sam', 'Samaritan Aramaic'), ('smi', 'Sami languages'), ('smo', 'Samoan'), ('sad', 'Sandawe'), ('sag', 'Sango'), ('san', 'Sanskrit'), ('sat', 'Santali'), ('srd', 'Sardinian'), ('sas', 'Sasak'), ('sco', 'Scots'), ('sel', 'Selkup'), ('sem', 'Semitic languages'), ('srp', 'Serbian'), ('srr', 'Serer'), ('shn', 'Shan'), ('sna', 'Shona'), ('iii', 'Sichuan Yi; Nuosu'), ('scn', 'Sicilian'), ('sid', 'Sidamo'), ('sgn', 'Sign Languages'), ('bla', 'Siksika'), ('snd', 'Sindhi'), ('sin', 'Sinhala; Sinhalese'), ('sit', 'Sino-Tibetan languages'), ('sio', 'Siouan languages'), ('sms', 'Skolt Sami'), ('den', 'Slave (Athapascan)'), ('sla', 'Slavic languages'), ('slk', 'Slovak'), ('slv', 'Slovenian'), ('sog', 'Sogdian'), ('som', 'Somali'), ('son', 'Songhai languages'), ('snk', 'Soninke'), ('wen', 'Sorbian languages'), ('sot', 'Sotho, Southern'), ('sai', 'South American Indian languages'), ('alt', 'Southern Altai'), ('sma', 'Southern Sami'), ('spa', 'Spanish; Castilian'), ('srn', 'Sranan Tongo'), ('zgh', 'Standard Moroccan Tamazight'), ('suk', 'Sukuma'), ('sux', 'Sumerian'), ('sun', 'Sundanese'), ('sus', 'Susu'), ('swa', 'Swahili'), ('ssw', 'Swati'), ('swe', 'Swedish'), ('gsw', 'Swiss German; Alemannic; Alsatian'), ('syr', 'Syriac'), ('tgl', 'Tagalog'), ('tah', 'Tahitian'), ('tai', 'Tai languages'), ('tgk', 'Tajik'), ('tmh', 'Tamashek'), ('tam', 'Tamil'), ('tat', 'Tatar'), ('tel', 'Telugu'), ('ter', 'Tereno'), ('tet', 'Tetum'), ('tha', 'Thai'), ('bod', 'Tibetan'), ('tig', 'Tigre'), ('tir', 'Tigrinya'), ('tem', 'Timne'), ('tiv', 'Tiv'), ('tli', 'Tlingit'), ('tpi', 'Tok Pisin'), ('tkl', 'Tokelau'), ('tog', 'Tonga (Nyasa)'), ('ton', 'Tonga (Tonga Islands)'), ('tsi', 'Tsimshian'), ('tso', 'Tsonga'), ('tsn', 'Tswana'), ('tum', 'Tumbuka'), ('tup', 'Tupi languages'), ('tur', 'Turkish'), ('ota', 'Turkish, Ottoman (1500-1928)'), ('tuk', 'Turkmen'), ('tvl', 'Tuvalu'), ('tyv', 'Tuvinian'), ('twi', 'Twi'), ('udm', 'Udmurt'), ('uga', 'Ugaritic'), ('uig', 'Uighur; Uyghur'), ('ukr', 'Ukrainian'), ('umb', 'Umbundu'), ('mis', 'Uncoded languages'), ('und', 'Undetermined'), ('hsb', 'Upper Sorbian'), ('urd', 'Urdu'), ('uzb', 'Uzbek'), ('vai', 'Vai'), ('ven', 'Venda'), ('vie', 'Vietnamese'), ('vol', 'Volapük'), ('vot', 'Votic'), ('wak', 'Wakashan languages'), ('wln', 'Walloon'), ('war', 'Waray'), ('was', 'Washo'), ('cym', 'Welsh'), ('fry', 'Western Frisian'), ('wal', 'Wolaitta; Wolaytta'), ('wol', 'Wolof'), ('xho', 'Xhosa'), ('sah', 'Yakut'), ('yao', 'Yao'), ('yap', 'Yapese'), ('yid', 'Yiddish'), ('yor', 'Yoruba'), ('ypk', 'Yupik languages'), ('znd', 'Zande languages'), ('zap', 'Zapotec'), ('zza', 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), ('zen', 'Zenaga'), ('zha', 'Zhuang; Chuang'), ('zul', 'Zulu'), ('zun', 'Zuni')], help_text='The default language of articles when lang is hidden', max_length=200, null=True), + model_name="submissionconfiguration", + name="default_language", + field=models.CharField( + choices=[ + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokmål, Norwegian; Norwegian Bokmål"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ("nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa"), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ( + "ina", + "Interlingua (International Auxiliary Language Association)", + ), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ( + "arc", + "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)", + ), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Provençal, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volapük"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), + ], + help_text="The default language of articles when lang is hidden", + max_length=200, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='default_license', - field=models.ForeignKey(help_text='The default license applied when no option is presented', null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Licence'), + model_name="submissionconfiguration", + name="default_license", + field=models.ForeignKey( + help_text="The default license applied when no option is presented", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Licence", + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='default_section', - field=models.ForeignKey(help_text='The default section of articles when no option is presented', null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Section'), + model_name="submissionconfiguration", + name="default_section", + field=models.ForeignKey( + help_text="The default section of articles when no option is presented", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Section", + ), ), migrations.AlterField( - model_name='article', - name='comments_editor', - field=models.TextField(blank=True, help_text="Add any comments you'd like the editor to consider here.", null=True, verbose_name='Comments to the Editor'), + model_name="article", + name="comments_editor", + field=models.TextField( + blank=True, + help_text="Add any comments you'd like the editor to consider here.", + null=True, + verbose_name="Comments to the Editor", + ), ), migrations.AlterField( - model_name='article', - name='competing_interests', - field=models.TextField(blank=True, help_text='If you have any competing or conflict of interests in the publication of this article please state them here.', null=True), + model_name="article", + name="competing_interests", + field=models.TextField( + blank=True, + help_text="If you have any competing or conflict of interests in the publication of this article please state them here.", + null=True, + ), ), ] diff --git a/src/submission/migrations/0026_auto_20180510_0845.py b/src/submission/migrations/0026_auto_20180510_0845.py index f6aea339a5..5045c09f26 100644 --- a/src/submission/migrations/0026_auto_20180510_0845.py +++ b/src/submission/migrations/0026_auto_20180510_0845.py @@ -6,20 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0025_auto_20180508_1138'), + ("submission", "0025_auto_20180508_1138"), ] operations = [ migrations.AddField( - model_name='submissionconfiguration', - name='section', + model_name="submissionconfiguration", + name="section", field=models.BooleanField(default=True), ), migrations.AlterField( - model_name='submissionconfiguration', - name='figures_data', - field=models.BooleanField(default=True, verbose_name='Figures and Data Files'), + model_name="submissionconfiguration", + name="figures_data", + field=models.BooleanField( + default=True, verbose_name="Figures and Data Files" + ), ), ] diff --git a/src/submission/migrations/0027_auto_20180806_1005.py b/src/submission/migrations/0027_auto_20180806_1005.py index 9e1eda2fbd..d9b0ced290 100644 --- a/src/submission/migrations/0027_auto_20180806_1005.py +++ b/src/submission/migrations/0027_auto_20180806_1005.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0026_auto_20180510_0845'), + ("submission", "0026_auto_20180510_0845"), ] operations = [ migrations.AlterField( - model_name='article', - name='abstract', + model_name="article", + name="abstract", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/submission/migrations/0028_auto_20181116_1144.py b/src/submission/migrations/0028_auto_20181116_1144.py index 6538902511..ee54a7979e 100644 --- a/src/submission/migrations/0028_auto_20181116_1144.py +++ b/src/submission/migrations/0028_auto_20181116_1144.py @@ -8,15 +8,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0027_auto_20180806_1005'), + ("submission", "0027_auto_20180806_1005"), ] operations = [ migrations.AlterField( - model_name='article', - name='meta_image', - field=models.ImageField(blank=True, null=True, storage=core.file_system.JanewayFileSystemStorage(), upload_to=submission.models.article_media_upload), + model_name="article", + name="meta_image", + field=models.ImageField( + blank=True, + null=True, + storage=core.file_system.JanewayFileSystemStorage(), + upload_to=submission.models.article_media_upload, + ), ), ] diff --git a/src/submission/migrations/0029_auto_20181128_1651.py b/src/submission/migrations/0029_auto_20181128_1651.py index 75a5dfb6a5..954407eef3 100644 --- a/src/submission/migrations/0029_auto_20181128_1651.py +++ b/src/submission/migrations/0029_auto_20181128_1651.py @@ -6,19 +6,20 @@ def create_missing_configurations(apps, schema_editor): - Journal = apps.get_model('journal', 'Journal') - SubmissionConfiguration = apps.get_model('submission', 'SubmissionConfiguration') + Journal = apps.get_model("journal", "Journal") + SubmissionConfiguration = apps.get_model("submission", "SubmissionConfiguration") for journal in Journal.objects.all(): SubmissionConfiguration.objects.get_or_create(journal=journal) class Migration(migrations.Migration): - dependencies = [ - ('submission', '0028_auto_20181116_1144'), + ("submission", "0028_auto_20181116_1144"), ] operations = [ - migrations.RunPython(create_missing_configurations, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + create_missing_configurations, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/submission/migrations/0030_rename_readable_stages.py b/src/submission/migrations/0030_rename_readable_stages.py index 867f943fac..0e8643eb36 100644 --- a/src/submission/migrations/0030_rename_readable_stages.py +++ b/src/submission/migrations/0030_rename_readable_stages.py @@ -6,15 +6,35 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0029_auto_20181128_1651'), + ("submission", "0029_auto_20181128_1651"), ] operations = [ migrations.AlterField( - model_name='article', - name='stage', - field=models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned to Editor'), ('Under Review', 'Peer Review'), ('Under Revision', 'Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published')], default='Unsubmitted', max_length=200), + model_name="article", + name="stage", + field=models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned to Editor"), + ("Under Review", "Peer Review"), + ("Under Revision", "Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ], + default="Unsubmitted", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0031_field_display.py b/src/submission/migrations/0031_field_display.py index 0b1940ab69..8ce6053e5b 100644 --- a/src/submission/migrations/0031_field_display.py +++ b/src/submission/migrations/0031_field_display.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0030_rename_readable_stages'), + ("submission", "0030_rename_readable_stages"), ] operations = [ migrations.AddField( - model_name='field', - name='display', - field=models.BooleanField(default=False, help_text='Whether or not display this field in the article page'), + model_name="field", + name="display", + field=models.BooleanField( + default=False, + help_text="Whether or not display this field in the article page", + ), ), ] diff --git a/src/submission/migrations/0032_auto_20190304_0916.py b/src/submission/migrations/0032_auto_20190304_0916.py index 2c8f3474a6..1f213291de 100644 --- a/src/submission/migrations/0032_auto_20190304_0916.py +++ b/src/submission/migrations/0032_auto_20190304_0916.py @@ -6,15 +6,36 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0031_field_display'), + ("submission", "0031_field_display"), ] operations = [ migrations.AlterField( - model_name='article', - name='stage', - field=models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned to Editor'), ('Under Review', 'Peer Review'), ('Under Revision', 'Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published'), ('Back Content', 'Back Content Plugin')], default='Unsubmitted', max_length=200), + model_name="article", + name="stage", + field=models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned to Editor"), + ("Under Review", "Peer Review"), + ("Under Revision", "Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ("Back Content", "Back Content Plugin"), + ], + default="Unsubmitted", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0033_frozenauthor_is_corporate.py b/src/submission/migrations/0033_frozenauthor_is_corporate.py index ecc819f7a1..246336f9fe 100644 --- a/src/submission/migrations/0033_frozenauthor_is_corporate.py +++ b/src/submission/migrations/0033_frozenauthor_is_corporate.py @@ -6,15 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0032_auto_20190304_0916'), + ("submission", "0032_auto_20190304_0916"), ] operations = [ migrations.AddField( - model_name='frozenauthor', - name='is_corporate', - field=models.BooleanField(default=False, help_text='If enabled, the institution and department fields will be used as the author full name'), + model_name="frozenauthor", + name="is_corporate", + field=models.BooleanField( + default=False, + help_text="If enabled, the institution and department fields will be used as the author full name", + ), ), ] diff --git a/src/submission/migrations/0034_auto_20190416_1009.py b/src/submission/migrations/0034_auto_20190416_1009.py index 4519ed749b..09171d0010 100644 --- a/src/submission/migrations/0034_auto_20190416_1009.py +++ b/src/submission/migrations/0034_auto_20190416_1009.py @@ -7,25 +7,539 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0033_frozenauthor_is_corporate'), + ("submission", "0033_frozenauthor_is_corporate"), ] operations = [ migrations.AlterField( - model_name='submissionconfiguration', - name='default_language', - field=models.CharField(blank=True, choices=[('eng', 'English'), ('abk', 'Abkhazian'), ('ace', 'Achinese'), ('ach', 'Acoli'), ('ada', 'Adangme'), ('ady', 'Adyghe; Adygei'), ('aar', 'Afar'), ('afh', 'Afrihili'), ('afr', 'Afrikaans'), ('afa', 'Afro-Asiatic languages'), ('ain', 'Ainu'), ('aka', 'Akan'), ('akk', 'Akkadian'), ('sqi', 'Albanian'), ('ale', 'Aleut'), ('alg', 'Algonquian languages'), ('tut', 'Altaic languages'), ('amh', 'Amharic'), ('anp', 'Angika'), ('apa', 'Apache languages'), ('ara', 'Arabic'), ('arg', 'Aragonese'), ('arp', 'Arapaho'), ('arw', 'Arawak'), ('hye', 'Armenian'), ('rup', 'Aromanian; Arumanian; Macedo-Romanian'), ('art', 'Artificial languages'), ('asm', 'Assamese'), ('ast', 'Asturian; Bable; Leonese; Asturleonese'), ('ath', 'Athapascan languages'), ('aus', 'Australian languages'), ('map', 'Austronesian languages'), ('ava', 'Avaric'), ('ave', 'Avestan'), ('awa', 'Awadhi'), ('aym', 'Aymara'), ('aze', 'Azerbaijani'), ('ban', 'Balinese'), ('bat', 'Baltic languages'), ('bal', 'Baluchi'), ('bam', 'Bambara'), ('bai', 'Bamileke languages'), ('bad', 'Banda languages'), ('bnt', 'Bantu languages'), ('bas', 'Basa'), ('bak', 'Bashkir'), ('eus', 'Basque'), ('btk', 'Batak languages'), ('bej', 'Beja; Bedawiyet'), ('bel', 'Belarusian'), ('bem', 'Bemba'), ('ben', 'Bengali'), ('ber', 'Berber languages'), ('bho', 'Bhojpuri'), ('bih', 'Bihari languages'), ('bik', 'Bikol'), ('bin', 'Bini; Edo'), ('bis', 'Bislama'), ('byn', 'Blin; Bilin'), ('zbl', 'Blissymbols; Blissymbolics; Bliss'), ('nob', 'Bokmål, Norwegian; Norwegian Bokmål'), ('bos', 'Bosnian'), ('bra', 'Braj'), ('bre', 'Breton'), ('bug', 'Buginese'), ('bul', 'Bulgarian'), ('bua', 'Buriat'), ('mya', 'Burmese'), ('cad', 'Caddo'), ('cat', 'Catalan; Valencian'), ('cau', 'Caucasian languages'), ('ceb', 'Cebuano'), ('cel', 'Celtic languages'), ('cai', 'Central American Indian languages'), ('khm', 'Central Khmer'), ('chg', 'Chagatai'), ('cmc', 'Chamic languages'), ('cha', 'Chamorro'), ('che', 'Chechen'), ('chr', 'Cherokee'), ('chy', 'Cheyenne'), ('chb', 'Chibcha'), ('nya', 'Chichewa; Chewa; Nyanja'), ('zho', 'Chinese'), ('chn', 'Chinook jargon'), ('chp', 'Chipewyan; Dene Suline'), ('cho', 'Choctaw'), ('chu', 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), ('chk', 'Chuukese'), ('chv', 'Chuvash'), ('nwc', 'Classical Newari; Old Newari; Classical Nepal Bhasa'), ('syc', 'Classical Syriac'), ('cop', 'Coptic'), ('cor', 'Cornish'), ('cos', 'Corsican'), ('cre', 'Cree'), ('mus', 'Creek'), ('crp', 'Creoles and pidgins'), ('cpe', 'Creoles and pidgins, English based'), ('cpf', 'Creoles and pidgins, French-based'), ('cpp', 'Creoles and pidgins, Portuguese-based'), ('crh', 'Crimean Tatar; Crimean Turkish'), ('hrv', 'Croatian'), ('cus', 'Cushitic languages'), ('ces', 'Czech'), ('dak', 'Dakota'), ('dan', 'Danish'), ('dar', 'Dargwa'), ('del', 'Delaware'), ('din', 'Dinka'), ('div', 'Divehi; Dhivehi; Maldivian'), ('doi', 'Dogri'), ('dgr', 'Dogrib'), ('dra', 'Dravidian languages'), ('dua', 'Duala'), ('dum', 'Dutch, Middle (ca. 1050-1350)'), ('nld', 'Dutch; Flemish'), ('dyu', 'Dyula'), ('dzo', 'Dzongkha'), ('frs', 'Eastern Frisian'), ('efi', 'Efik'), ('egy', 'Egyptian (Ancient)'), ('eka', 'Ekajuk'), ('elx', 'Elamite'), ('enm', 'English, Middle (1100-1500)'), ('ang', 'English, Old (ca. 450-1100)'), ('myv', 'Erzya'), ('epo', 'Esperanto'), ('est', 'Estonian'), ('ewe', 'Ewe'), ('ewo', 'Ewondo'), ('fan', 'Fang'), ('fat', 'Fanti'), ('fao', 'Faroese'), ('fij', 'Fijian'), ('fil', 'Filipino; Pilipino'), ('fin', 'Finnish'), ('fiu', 'Finno-Ugrian languages'), ('fon', 'Fon'), ('fra', 'French'), ('frm', 'French, Middle (ca. 1400-1600)'), ('fro', 'French, Old (842-ca. 1400)'), ('fur', 'Friulian'), ('ful', 'Fulah'), ('gaa', 'Ga'), ('gla', 'Gaelic; Scottish Gaelic'), ('car', 'Galibi Carib'), ('glg', 'Galician'), ('lug', 'Ganda'), ('gay', 'Gayo'), ('gba', 'Gbaya'), ('gez', 'Geez'), ('kat', 'Georgian'), ('deu', 'German'), ('gmh', 'German, Middle High (ca. 1050-1500)'), ('goh', 'German, Old High (ca. 750-1050)'), ('gem', 'Germanic languages'), ('gil', 'Gilbertese'), ('gon', 'Gondi'), ('gor', 'Gorontalo'), ('got', 'Gothic'), ('grb', 'Grebo'), ('grc', 'Greek, Ancient (to 1453)'), ('ell', 'Greek, Modern (1453-)'), ('grn', 'Guarani'), ('guj', 'Gujarati'), ('gwi', "Gwich'in"), ('hai', 'Haida'), ('hat', 'Haitian; Haitian Creole'), ('hau', 'Hausa'), ('haw', 'Hawaiian'), ('heb', 'Hebrew'), ('her', 'Herero'), ('hil', 'Hiligaynon'), ('him', 'Himachali languages; Western Pahari languages'), ('hin', 'Hindi'), ('hmo', 'Hiri Motu'), ('hit', 'Hittite'), ('hmn', 'Hmong; Mong'), ('hun', 'Hungarian'), ('hup', 'Hupa'), ('iba', 'Iban'), ('isl', 'Icelandic'), ('ido', 'Ido'), ('ibo', 'Igbo'), ('ijo', 'Ijo languages'), ('ilo', 'Iloko'), ('smn', 'Inari Sami'), ('inc', 'Indic languages'), ('ine', 'Indo-European languages'), ('ind', 'Indonesian'), ('inh', 'Ingush'), ('ina', 'Interlingua (International Auxiliary Language Association)'), ('ile', 'Interlingue; Occidental'), ('iku', 'Inuktitut'), ('ipk', 'Inupiaq'), ('ira', 'Iranian languages'), ('gle', 'Irish'), ('mga', 'Irish, Middle (900-1200)'), ('sga', 'Irish, Old (to 900)'), ('iro', 'Iroquoian languages'), ('ita', 'Italian'), ('jpn', 'Japanese'), ('jav', 'Javanese'), ('jrb', 'Judeo-Arabic'), ('jpr', 'Judeo-Persian'), ('kbd', 'Kabardian'), ('kab', 'Kabyle'), ('kac', 'Kachin; Jingpho'), ('kal', 'Kalaallisut; Greenlandic'), ('xal', 'Kalmyk; Oirat'), ('kam', 'Kamba'), ('kan', 'Kannada'), ('kau', 'Kanuri'), ('kaa', 'Kara-Kalpak'), ('krc', 'Karachay-Balkar'), ('krl', 'Karelian'), ('kar', 'Karen languages'), ('kas', 'Kashmiri'), ('csb', 'Kashubian'), ('kaw', 'Kawi'), ('kaz', 'Kazakh'), ('kha', 'Khasi'), ('khi', 'Khoisan languages'), ('kho', 'Khotanese;Sakan'), ('kik', 'Kikuyu; Gikuyu'), ('kmb', 'Kimbundu'), ('kin', 'Kinyarwanda'), ('kir', 'Kirghiz; Kyrgyz'), ('tlh', 'Klingon; tlhIngan-Hol'), ('kom', 'Komi'), ('kon', 'Kongo'), ('kok', 'Konkani'), ('kor', 'Korean'), ('kos', 'Kosraean'), ('kpe', 'Kpelle'), ('kro', 'Kru languages'), ('kua', 'Kuanyama; Kwanyama'), ('kum', 'Kumyk'), ('kur', 'Kurdish'), ('kru', 'Kurukh'), ('kut', 'Kutenai'), ('lad', 'Ladino'), ('lah', 'Lahnda'), ('lam', 'Lamba'), ('day', 'Land Dayak languages'), ('lao', 'Lao'), ('lat', 'Latin'), ('lav', 'Latvian'), ('lez', 'Lezghian'), ('lim', 'Limburgan; Limburger; Limburgish'), ('lin', 'Lingala'), ('lit', 'Lithuanian'), ('jbo', 'Lojban'), ('nds', 'Low German; Low Saxon; German, Low; Saxon, Low'), ('dsb', 'Lower Sorbian'), ('loz', 'Lozi'), ('lub', 'Luba-Katanga'), ('lua', 'Luba-Lulua'), ('lui', 'Luiseno'), ('smj', 'Lule Sami'), ('lun', 'Lunda'), ('luo', 'Luo (Kenya and Tanzania)'), ('lus', 'Lushai'), ('ltz', 'Luxembourgish; Letzeburgesch'), ('mkd', 'Macedonian'), ('mad', 'Madurese'), ('mag', 'Magahi'), ('mai', 'Maithili'), ('mak', 'Makasar'), ('mlg', 'Malagasy'), ('msa', 'Malay'), ('mal', 'Malayalam'), ('mlt', 'Maltese'), ('mnc', 'Manchu'), ('mdr', 'Mandar'), ('man', 'Mandingo'), ('mni', 'Manipuri'), ('mno', 'Manobo languages'), ('glv', 'Manx'), ('mri', 'Maori'), ('arn', 'Mapudungun; Mapuche'), ('mar', 'Marathi'), ('chm', 'Mari'), ('mah', 'Marshallese'), ('mwr', 'Marwari'), ('mas', 'Masai'), ('myn', 'Mayan languages'), ('men', 'Mende'), ('mic', "Mi'kmaq; Micmac"), ('min', 'Minangkabau'), ('mwl', 'Mirandese'), ('moh', 'Mohawk'), ('mdf', 'Moksha'), ('mol', 'Moldavian; Moldovan'), ('mkh', 'Mon-Khmer languages'), ('lol', 'Mongo'), ('mon', 'Mongolian'), ('mos', 'Mossi'), ('mul', 'Multiple languages'), ('mun', 'Munda languages'), ('nqo', "N'Ko"), ('nah', 'Nahuatl languages'), ('nau', 'Nauru'), ('nav', 'Navajo; Navaho'), ('nde', 'Ndebele, North; North Ndebele'), ('nbl', 'Ndebele, South; South Ndebele'), ('ndo', 'Ndonga'), ('nap', 'Neapolitan'), ('new', 'Nepal Bhasa; Newari'), ('nep', 'Nepali'), ('nia', 'Nias'), ('nic', 'Niger-Kordofanian languages'), ('ssa', 'Nilo-Saharan languages'), ('niu', 'Niuean'), ('zxx', 'No linguistic content; Not applicable'), ('nog', 'Nogai'), ('non', 'Norse, Old'), ('nai', 'North American Indian languages'), ('frr', 'Northern Frisian'), ('sme', 'Northern Sami'), ('nor', 'Norwegian'), ('nno', 'Norwegian Nynorsk; Nynorsk, Norwegian'), ('nub', 'Nubian languages'), ('nym', 'Nyamwezi'), ('nyn', 'Nyankole'), ('nyo', 'Nyoro'), ('nzi', 'Nzima'), ('oci', 'Occitan (post 1500)'), ('arc', 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), ('oji', 'Ojibwa'), ('ori', 'Oriya'), ('orm', 'Oromo'), ('osa', 'Osage'), ('oss', 'Ossetian; Ossetic'), ('oto', 'Otomian languages'), ('pal', 'Pahlavi'), ('pau', 'Palauan'), ('pli', 'Pali'), ('pam', 'Pampanga; Kapampangan'), ('pag', 'Pangasinan'), ('pan', 'Panjabi; Punjabi'), ('pap', 'Papiamento'), ('paa', 'Papuan languages'), ('nso', 'Pedi; Sepedi; Northern Sotho'), ('fas', 'Persian'), ('peo', 'Persian, Old (ca. 600-400 B.C.)'), ('phi', 'Philippine languages'), ('phn', 'Phoenician'), ('pon', 'Pohnpeian'), ('pol', 'Polish'), ('por', 'Portuguese'), ('pra', 'Prakrit languages'), ('pro', 'Provençal, Old (to 1500); Occitan, Old (to 1500)'), ('pus', 'Pushto; Pashto'), ('que', 'Quechua'), ('raj', 'Rajasthani'), ('rap', 'Rapanui'), ('rar', 'Rarotongan; Cook Islands Maori'), ('qaa-qtz', 'Reserved for local use'), ('roa', 'Romance languages'), ('ron', 'Romanian'), ('roh', 'Romansh'), ('rom', 'Romany'), ('run', 'Rundi'), ('rus', 'Russian'), ('sal', 'Salishan languages'), ('sam', 'Samaritan Aramaic'), ('smi', 'Sami languages'), ('smo', 'Samoan'), ('sad', 'Sandawe'), ('sag', 'Sango'), ('san', 'Sanskrit'), ('sat', 'Santali'), ('srd', 'Sardinian'), ('sas', 'Sasak'), ('sco', 'Scots'), ('sel', 'Selkup'), ('sem', 'Semitic languages'), ('srp', 'Serbian'), ('srr', 'Serer'), ('shn', 'Shan'), ('sna', 'Shona'), ('iii', 'Sichuan Yi; Nuosu'), ('scn', 'Sicilian'), ('sid', 'Sidamo'), ('sgn', 'Sign Languages'), ('bla', 'Siksika'), ('snd', 'Sindhi'), ('sin', 'Sinhala; Sinhalese'), ('sit', 'Sino-Tibetan languages'), ('sio', 'Siouan languages'), ('sms', 'Skolt Sami'), ('den', 'Slave (Athapascan)'), ('sla', 'Slavic languages'), ('slk', 'Slovak'), ('slv', 'Slovenian'), ('sog', 'Sogdian'), ('som', 'Somali'), ('son', 'Songhai languages'), ('snk', 'Soninke'), ('wen', 'Sorbian languages'), ('sot', 'Sotho, Southern'), ('sai', 'South American Indian languages'), ('alt', 'Southern Altai'), ('sma', 'Southern Sami'), ('spa', 'Spanish; Castilian'), ('srn', 'Sranan Tongo'), ('zgh', 'Standard Moroccan Tamazight'), ('suk', 'Sukuma'), ('sux', 'Sumerian'), ('sun', 'Sundanese'), ('sus', 'Susu'), ('swa', 'Swahili'), ('ssw', 'Swati'), ('swe', 'Swedish'), ('gsw', 'Swiss German; Alemannic; Alsatian'), ('syr', 'Syriac'), ('tgl', 'Tagalog'), ('tah', 'Tahitian'), ('tai', 'Tai languages'), ('tgk', 'Tajik'), ('tmh', 'Tamashek'), ('tam', 'Tamil'), ('tat', 'Tatar'), ('tel', 'Telugu'), ('ter', 'Tereno'), ('tet', 'Tetum'), ('tha', 'Thai'), ('bod', 'Tibetan'), ('tig', 'Tigre'), ('tir', 'Tigrinya'), ('tem', 'Timne'), ('tiv', 'Tiv'), ('tli', 'Tlingit'), ('tpi', 'Tok Pisin'), ('tkl', 'Tokelau'), ('tog', 'Tonga (Nyasa)'), ('ton', 'Tonga (Tonga Islands)'), ('tsi', 'Tsimshian'), ('tso', 'Tsonga'), ('tsn', 'Tswana'), ('tum', 'Tumbuka'), ('tup', 'Tupi languages'), ('tur', 'Turkish'), ('ota', 'Turkish, Ottoman (1500-1928)'), ('tuk', 'Turkmen'), ('tvl', 'Tuvalu'), ('tyv', 'Tuvinian'), ('twi', 'Twi'), ('udm', 'Udmurt'), ('uga', 'Ugaritic'), ('uig', 'Uighur; Uyghur'), ('ukr', 'Ukrainian'), ('umb', 'Umbundu'), ('mis', 'Uncoded languages'), ('und', 'Undetermined'), ('hsb', 'Upper Sorbian'), ('urd', 'Urdu'), ('uzb', 'Uzbek'), ('vai', 'Vai'), ('ven', 'Venda'), ('vie', 'Vietnamese'), ('vol', 'Volapük'), ('vot', 'Votic'), ('wak', 'Wakashan languages'), ('wln', 'Walloon'), ('war', 'Waray'), ('was', 'Washo'), ('cym', 'Welsh'), ('fry', 'Western Frisian'), ('wal', 'Wolaitta; Wolaytta'), ('wol', 'Wolof'), ('xho', 'Xhosa'), ('sah', 'Yakut'), ('yao', 'Yao'), ('yap', 'Yapese'), ('yid', 'Yiddish'), ('yor', 'Yoruba'), ('ypk', 'Yupik languages'), ('znd', 'Zande languages'), ('zap', 'Zapotec'), ('zza', 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), ('zen', 'Zenaga'), ('zha', 'Zhuang; Chuang'), ('zul', 'Zulu'), ('zun', 'Zuni')], help_text='The default language of articles when lang is hidden', max_length=200, null=True), + model_name="submissionconfiguration", + name="default_language", + field=models.CharField( + blank=True, + choices=[ + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokmål, Norwegian; Norwegian Bokmål"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ("nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa"), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ( + "ina", + "Interlingua (International Auxiliary Language Association)", + ), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ( + "arc", + "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)", + ), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Provençal, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volapük"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), + ], + help_text="The default language of articles when lang is hidden", + max_length=200, + null=True, + ), ), migrations.AlterField( - model_name='submissionconfiguration', - name='default_license', - field=models.ForeignKey(blank=True, help_text='The default license applied when no option is presented', null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Licence'), + model_name="submissionconfiguration", + name="default_license", + field=models.ForeignKey( + blank=True, + help_text="The default license applied when no option is presented", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Licence", + ), ), migrations.AlterField( - model_name='submissionconfiguration', - name='default_section', - field=models.ForeignKey(blank=True, help_text='The default section of articles when no option is presented', null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.Section'), + model_name="submissionconfiguration", + name="default_section", + field=models.ForeignKey( + blank=True, + help_text="The default section of articles when no option is presented", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.Section", + ), ), ] diff --git a/src/submission/migrations/0035_auto_20190627_1331.py b/src/submission/migrations/0035_auto_20190627_1331.py index 52ca058986..65494fd8e3 100644 --- a/src/submission/migrations/0035_auto_20190627_1331.py +++ b/src/submission/migrations/0035_auto_20190627_1331.py @@ -6,15 +6,35 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0034_auto_20190416_1009'), + ("submission", "0034_auto_20190416_1009"), ] operations = [ migrations.AlterField( - model_name='article', - name='stage', - field=models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned to Editor'), ('Under Review', 'Peer Review'), ('Under Revision', 'Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published')], default='Unsubmitted', max_length=200), + model_name="article", + name="stage", + field=models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned to Editor"), + ("Under Review", "Peer Review"), + ("Under Revision", "Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ], + default="Unsubmitted", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0035_auto_20190712_2015.py b/src/submission/migrations/0035_auto_20190712_2015.py index 70f6f0861e..0ab1415f9c 100644 --- a/src/submission/migrations/0035_auto_20190712_2015.py +++ b/src/submission/migrations/0035_auto_20190712_2015.py @@ -6,20 +6,44 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0034_auto_20190416_1009'), + ("submission", "0034_auto_20190416_1009"), ] operations = [ migrations.AlterField( - model_name='article', - name='abstract', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='stage', - field=models.CharField(choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned to Editor'), ('Under Review', 'Peer Review'), ('Under Revision', 'Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published')], default='Unsubmitted', max_length=200), + model_name="article", + name="stage", + field=models.CharField( + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned to Editor"), + ("Under Review", "Peer Review"), + ("Under Revision", "Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ], + default="Unsubmitted", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0036_article_source_files.py b/src/submission/migrations/0036_article_source_files.py index 66dfa0df6e..063c3a3383 100644 --- a/src/submission/migrations/0036_article_source_files.py +++ b/src/submission/migrations/0036_article_source_files.py @@ -6,16 +6,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0030_merge_20190405_1549'), - ('submission', '0035_auto_20190712_2015'), + ("core", "0030_merge_20190405_1549"), + ("submission", "0035_auto_20190712_2015"), ] operations = [ migrations.AddField( - model_name='article', - name='source_files', - field=models.ManyToManyField(blank=True, related_name='source_files', to='core.File'), + model_name="article", + name="source_files", + field=models.ManyToManyField( + blank=True, related_name="source_files", to="core.File" + ), ), ] diff --git a/src/submission/migrations/0037_auto_20190808_1326.py b/src/submission/migrations/0037_auto_20190808_1326.py index 16a3e0874b..3951929230 100644 --- a/src/submission/migrations/0037_auto_20190808_1326.py +++ b/src/submission/migrations/0037_auto_20190808_1326.py @@ -6,9 +6,8 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0036_article_source_files'), + ("submission", "0036_article_source_files"), ] # Empty migration for backwards compatibility diff --git a/src/submission/migrations/0038_merge_20190812_1614.py b/src/submission/migrations/0038_merge_20190812_1614.py index 9ea5f2b9bb..2711f0503d 100644 --- a/src/submission/migrations/0038_merge_20190812_1614.py +++ b/src/submission/migrations/0038_merge_20190812_1614.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0037_auto_20190808_1326'), - ('submission', '0035_auto_20190627_1331'), + ("submission", "0037_auto_20190808_1326"), + ("submission", "0035_auto_20190627_1331"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0039_auto_20191115_1253.py b/src/submission/migrations/0039_auto_20191115_1253.py index 07549773c8..7646d4d669 100644 --- a/src/submission/migrations/0039_auto_20191115_1253.py +++ b/src/submission/migrations/0039_auto_20191115_1253.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0038_merge_20190812_1614'), + ("submission", "0038_merge_20190812_1614"), ] operations = [ migrations.AlterField( - model_name='article', - name='competing_interests', - field=models.TextField(blank=True, help_text='If you have any conflict of interests in the publication of this article please state them here.', null=True), + model_name="article", + name="competing_interests", + field=models.TextField( + blank=True, + help_text="If you have any conflict of interests in the publication of this article please state them here.", + null=True, + ), ), ] diff --git a/src/submission/migrations/0040_article_projected_issue.py b/src/submission/migrations/0040_article_projected_issue.py index 3e02fa409c..6f828db996 100644 --- a/src/submission/migrations/0040_article_projected_issue.py +++ b/src/submission/migrations/0040_article_projected_issue.py @@ -7,26 +7,25 @@ class Migration(migrations.Migration): - dependencies = [ - ('journal', '0037_auto_20200116_1201'), - ('submission', '0039_auto_20191115_1253'), + ("journal", "0037_auto_20200116_1201"), + ("submission", "0039_auto_20191115_1253"), ] operations = [ migrations.AddField( - model_name='article', - name='projected_issue', + model_name="article", + name="projected_issue", field=models.ForeignKey( - to='journal.Issue', + to="journal.Issue", blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, - related_name='projected_issue', - help_text='This field is for internal purposes only ' - 'before publication. You can use it to ' - 'track likely issue assignment before formally ' - 'assigning an issue.', + related_name="projected_issue", + help_text="This field is for internal purposes only " + "before publication. You can use it to " + "track likely issue assignment before formally " + "assigning an issue.", ), ), ] diff --git a/src/submission/migrations/0041_submission_stage_dynamic.py b/src/submission/migrations/0041_submission_stage_dynamic.py index b6d05aa5ab..d1a7a81d79 100644 --- a/src/submission/migrations/0041_submission_stage_dynamic.py +++ b/src/submission/migrations/0041_submission_stage_dynamic.py @@ -7,15 +7,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0040_article_projected_issue'), + ("submission", "0040_article_projected_issue"), ] operations = [ migrations.AlterField( - model_name='article', - name='stage', - field=submission.models.DynamicChoiceField(blank=True, choices=[('Unsubmitted', 'Unsubmitted'), ('Unassigned', 'Unassigned'), ('Assigned', 'Assigned to Editor'), ('Under Review', 'Peer Review'), ('Under Revision', 'Revision'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted'), ('Editor Copyediting', 'Editor Copyediting'), ('Author Copyediting', 'Author Copyediting'), ('Final Copyediting', 'Final Copyediting'), ('Typesetting', 'Typesetting'), ('Proofing', 'Proofing'), ('pre_publication', 'Pre Publication'), ('Published', 'Published'), ('preprint_review', 'Preprint Review'), ('preprint_published', 'Preprint Published'), ('Archived', 'Archived')], default='Unsubmitted', help_text="WARNING: Manually changing the stage of a submission overrides Janeway's workflow. It should only be changed to a value which is know to be safe such as a stage an article has already been a part of before.", max_length=200), + model_name="article", + name="stage", + field=submission.models.DynamicChoiceField( + blank=True, + choices=[ + ("Unsubmitted", "Unsubmitted"), + ("Unassigned", "Unassigned"), + ("Assigned", "Assigned to Editor"), + ("Under Review", "Peer Review"), + ("Under Revision", "Revision"), + ("Rejected", "Rejected"), + ("Accepted", "Accepted"), + ("Editor Copyediting", "Editor Copyediting"), + ("Author Copyediting", "Author Copyediting"), + ("Final Copyediting", "Final Copyediting"), + ("Typesetting", "Typesetting"), + ("Proofing", "Proofing"), + ("pre_publication", "Pre Publication"), + ("Published", "Published"), + ("preprint_review", "Preprint Review"), + ("preprint_published", "Preprint Published"), + ("Archived", "Archived"), + ], + default="Unsubmitted", + help_text="WARNING: Manually changing the stage of a submission overrides Janeway's workflow. It should only be changed to a value which is know to be safe such as a stage an article has already been a part of before.", + max_length=200, + ), ), ] diff --git a/src/submission/migrations/0042_auto_20200423_1534.py b/src/submission/migrations/0042_auto_20200423_1534.py index 7f8a95af0c..bf09b1e30c 100644 --- a/src/submission/migrations/0042_auto_20200423_1534.py +++ b/src/submission/migrations/0042_auto_20200423_1534.py @@ -7,24 +7,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0041_submission_stage_dynamic'), + ("submission", "0041_submission_stage_dynamic"), ] operations = [ migrations.AlterField( - model_name='article', - name='primary_issue', + model_name="article", + name="primary_issue", field=models.ForeignKey( - to='journal.Issue', + to="journal.Issue", blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, - help_text='You can only assign an issue ' - 'that this article is part of. ' - 'You can add this article to an ' - 'issue from the Issue Manager.', + help_text="You can only assign an issue " + "that this article is part of. " + "You can add this article to an " + "issue from the Issue Manager.", ), ), ] diff --git a/src/submission/migrations/0043_article_custom_how_to_cite.py b/src/submission/migrations/0043_article_custom_how_to_cite.py index 71c7ffcf57..3a77b1dd2d 100644 --- a/src/submission/migrations/0043_article_custom_how_to_cite.py +++ b/src/submission/migrations/0043_article_custom_how_to_cite.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0042_auto_20200423_1534'), + ("submission", "0042_auto_20200423_1534"), ] operations = [ migrations.AddField( - model_name='article', - name='custom_how_to_cite', - field=models.TextField(blank=True, help_text="Custom 'how to cite' text. To be used only if the block generated by Janeway is not suitable.", null=True), + model_name="article", + name="custom_how_to_cite", + field=models.TextField( + blank=True, + help_text="Custom 'how to cite' text. To be used only if the block generated by Janeway is not suitable.", + null=True, + ), ), ] diff --git a/src/submission/migrations/0043_auto_20200523_1158.py b/src/submission/migrations/0043_auto_20200523_1158.py index 49493e101f..107ccd21e7 100644 --- a/src/submission/migrations/0043_auto_20200523_1158.py +++ b/src/submission/migrations/0043_auto_20200523_1158.py @@ -6,23 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0042_auto_20200423_1534'), + ("submission", "0042_auto_20200423_1534"), ] operations = [ migrations.AlterModelManagers( - name='article', - managers=[ - ], + name="article", + managers=[], ), migrations.RemoveField( - model_name='article', - name='is_preprint', + model_name="article", + name="is_preprint", ), migrations.RemoveField( - model_name='article', - name='preprint_decision_notification', + model_name="article", + name="preprint_decision_notification", ), ] diff --git a/src/submission/migrations/0044_auto_20200526_1010.py b/src/submission/migrations/0044_auto_20200526_1010.py index abe0eaf816..0ab89242d5 100644 --- a/src/submission/migrations/0044_auto_20200526_1010.py +++ b/src/submission/migrations/0044_auto_20200526_1010.py @@ -6,40 +6,56 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0043_article_custom_how_to_cite'), + ("submission", "0043_article_custom_how_to_cite"), ] operations = [ migrations.CreateModel( - name='Funder', + name="Funder", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField( - max_length=500, - help_text='Funder name', - )), - ('fundref_id', models.CharField( - blank=True, - help_text='Funder DOI (optional). Enter as a full Uniform Resource Identifier (URI), such as http://dx.doi.org/10.13039/501100021082', - max_length=500, - null=True, - )), - ('funding_id', models.CharField( - blank=True, - help_text='The grant ID (optional). Enter the ID by itself', - max_length=500, - null=True, - )), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + max_length=500, + help_text="Funder name", + ), + ), + ( + "fundref_id", + models.CharField( + blank=True, + help_text="Funder DOI (optional). Enter as a full Uniform Resource Identifier (URI), such as http://dx.doi.org/10.13039/501100021082", + max_length=500, + null=True, + ), + ), + ( + "funding_id", + models.CharField( + blank=True, + help_text="The grant ID (optional). Enter the ID by itself", + max_length=500, + null=True, + ), + ), ], options={ - 'ordering': ('name',), + "ordering": ("name",), }, ), migrations.AddField( - model_name='article', - name='funders', - field=models.ManyToManyField(blank=True, to='submission.Funder'), + model_name="article", + name="funders", + field=models.ManyToManyField(blank=True, to="submission.Funder"), ), ] diff --git a/src/submission/migrations/0045_auto_20200528_1204.py b/src/submission/migrations/0045_auto_20200528_1204.py index 3aa840e463..1e8bba0bda 100644 --- a/src/submission/migrations/0045_auto_20200528_1204.py +++ b/src/submission/migrations/0045_auto_20200528_1204.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0044_auto_20200526_1010'), + ("submission", "0044_auto_20200526_1010"), ] operations = [ migrations.AddField( - model_name='submissionconfiguration', - name='funding', + model_name="submissionconfiguration", + name="funding", field=models.BooleanField(default=False), ), ] diff --git a/src/submission/migrations/0045_auto_20200529_1415.py b/src/submission/migrations/0045_auto_20200529_1415.py index 643b8d240c..1e928aef45 100644 --- a/src/submission/migrations/0045_auto_20200529_1415.py +++ b/src/submission/migrations/0045_auto_20200529_1415.py @@ -6,19 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0044_auto_20200526_1010'), + ("submission", "0044_auto_20200526_1010"), ] operations = [ migrations.AlterModelOptions( - name='frozenauthor', - options={'ordering': ('order', 'pk')}, + name="frozenauthor", + options={"ordering": ("order", "pk")}, ), migrations.AlterField( - model_name='submissionconfiguration', - name='subtitle', + model_name="submissionconfiguration", + name="subtitle", field=models.BooleanField(default=False), ), ] diff --git a/src/submission/migrations/0046_merge_20200604_1040.py b/src/submission/migrations/0046_merge_20200604_1040.py index 8d71eaa89c..8307d653f0 100644 --- a/src/submission/migrations/0046_merge_20200604_1040.py +++ b/src/submission/migrations/0046_merge_20200604_1040.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0045_auto_20200528_1204'), - ('submission', '0045_auto_20200529_1415'), + ("submission", "0045_auto_20200528_1204"), + ("submission", "0045_auto_20200529_1415"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0047_auto_20200720_1415.py b/src/submission/migrations/0047_auto_20200720_1415.py index 03a782e26f..e22dfe7371 100644 --- a/src/submission/migrations/0047_auto_20200720_1415.py +++ b/src/submission/migrations/0047_auto_20200720_1415.py @@ -6,20 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0046_merge_20200604_1040'), + ("submission", "0046_merge_20200604_1040"), ] operations = [ migrations.AlterField( - model_name='article', - name='subtitle', - field=models.CharField(blank=True, help_text='Do not use--deprecated in version 1.4.1 and later.', max_length=999, null=True), + model_name="article", + name="subtitle", + field=models.CharField( + blank=True, + help_text="Do not use--deprecated in version 1.4.1 and later.", + max_length=999, + null=True, + ), ), migrations.AlterField( - model_name='article', - name='title', - field=models.CharField(help_text='Your article title', max_length=999), + model_name="article", + name="title", + field=models.CharField(help_text="Your article title", max_length=999), ), ] diff --git a/src/submission/migrations/0047_merge_20200626_1336.py b/src/submission/migrations/0047_merge_20200626_1336.py index 4bfd4dac4e..0ece5ea197 100644 --- a/src/submission/migrations/0047_merge_20200626_1336.py +++ b/src/submission/migrations/0047_merge_20200626_1336.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0043_auto_20200523_1158'), - ('submission', '0046_merge_20200604_1040'), + ("submission", "0043_auto_20200523_1158"), + ("submission", "0046_merge_20200604_1040"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0048_submissionconfiguration_submission_file_text.py b/src/submission/migrations/0048_submissionconfiguration_submission_file_text.py index 167853c962..3f2a1a5ba7 100644 --- a/src/submission/migrations/0048_submissionconfiguration_submission_file_text.py +++ b/src/submission/migrations/0048_submissionconfiguration_submission_file_text.py @@ -6,15 +6,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0047_auto_20200720_1415'), + ("submission", "0047_auto_20200720_1415"), ] operations = [ migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255), + model_name="submissionconfiguration", + name="submission_file_text", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + ), ), ] diff --git a/src/submission/migrations/0049_auto_20201117_1904.py b/src/submission/migrations/0049_auto_20201117_1904.py index 57e40487fe..9feb9eb80e 100644 --- a/src/submission/migrations/0049_auto_20201117_1904.py +++ b/src/submission/migrations/0049_auto_20201117_1904.py @@ -7,45 +7,68 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0048_submissionconfiguration_submission_file_text'), + ("submission", "0048_submissionconfiguration_submission_file_text"), ] operations = [ migrations.AddField( - model_name='section', - name='auto_assign_editors', - field=models.BooleanField(default=False, help_text='Articles submitted to this section will be automatically assigned to the editors and/or section editors selected above.'), + model_name="section", + name="auto_assign_editors", + field=models.BooleanField( + default=False, + help_text="Articles submitted to this section will be automatically assigned to the editors and/or section editors selected above.", + ), ), migrations.AlterField( - model_name='section', - name='editors', - field=models.ManyToManyField(help_text='Editors assigned will be notified of submissions, overruling the notification settings for the journal.', to=settings.AUTH_USER_MODEL), + model_name="section", + name="editors", + field=models.ManyToManyField( + help_text="Editors assigned will be notified of submissions, overruling the notification settings for the journal.", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='section', - name='indexing', - field=models.BooleanField(default=True, help_text='Whether this section is put forward for indexing'), + model_name="section", + name="indexing", + field=models.BooleanField( + default=True, + help_text="Whether this section is put forward for indexing", + ), ), migrations.AlterField( - model_name='section', - name='is_filterable', - field=models.BooleanField(default=True, help_text='Allows filtering article search results by this section.'), + model_name="section", + name="is_filterable", + field=models.BooleanField( + default=True, + help_text="Allows filtering article search results by this section.", + ), ), migrations.AlterField( - model_name='section', - name='section_editors', - field=models.ManyToManyField(help_text='Section editors assigned will be notified of submissions, overruling the notification settings for the journal.', related_name='section_editors', to=settings.AUTH_USER_MODEL), + model_name="section", + name="section_editors", + field=models.ManyToManyField( + help_text="Section editors assigned will be notified of submissions, overruling the notification settings for the journal.", + related_name="section_editors", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='section', - name='sequence', - field=models.PositiveIntegerField(default=0, help_text='Determines the order in which the section is rendered Sections can also be reorder by drag-and-drop'), + model_name="section", + name="sequence", + field=models.PositiveIntegerField( + default=0, + help_text="Determines the order in which the section is rendered Sections can also be reorder by drag-and-drop", + ), ), migrations.AlterField( - model_name='sectiontranslation', - name='plural', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', max_length=200, null=True), + model_name="sectiontranslation", + name="plural", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), ] diff --git a/src/submission/migrations/0050_support_ordered_keywords.py b/src/submission/migrations/0050_support_ordered_keywords.py index baef6338aa..248213659c 100644 --- a/src/submission/migrations/0050_support_ordered_keywords.py +++ b/src/submission/migrations/0050_support_ordered_keywords.py @@ -8,12 +8,12 @@ def migrate_keywords(apps, schema_editor): - Article = apps.get_model('submission', 'Article') - KeywordArticle = apps.get_model('submission', 'KeywordArticle') + Article = apps.get_model("submission", "Article") + KeywordArticle = apps.get_model("submission", "KeywordArticle") articles = Article.objects.filter(keywords__isnull=False) for article in Article.objects.all(): - for i,kw in enumerate(article.keywords.all()): + for i, kw in enumerate(article.keywords.all()): kw_article, _ = KeywordArticle.objects.get_or_create( keyword=kw, article=article, @@ -23,62 +23,78 @@ def migrate_keywords(apps, schema_editor): def demigrate_keywords(apps, schema_editor): - KeywordArticle = apps.get_model('submission', 'KeywordArticle') + KeywordArticle = apps.get_model("submission", "KeywordArticle") for kw_article in KeywordArticle.objects.all(): kw_article.article.keywords.add(kw_article.keyword) class Migration(migrations.Migration): - dependencies = [ - ('submission', '0049_auto_20201117_1904'), + ("submission", "0049_auto_20201117_1904"), ] operations = [ # Allow usage of Article.objects migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), - ('objects', submission.models.ArticleManager()), + ("allarticles", django.db.models.manager.Manager()), + ("objects", submission.models.ArticleManager()), ], ), # Create new through model migrations.CreateModel( - name='KeywordArticle', + name="KeywordArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(default=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("order", models.PositiveIntegerField(default=1)), ], options={ - 'ordering': ['order'], + "ordering": ["order"], }, ), migrations.AddField( - model_name='keywordarticle', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Article'), + model_name="keywordarticle", + name="article", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Article" + ), ), migrations.AddField( - model_name='keywordarticle', - name='keyword', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.Keyword'), + model_name="keywordarticle", + name="keyword", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="submission.Keyword" + ), ), migrations.AlterUniqueTogether( - name='keywordarticle', - unique_together=set([('keyword', 'article')]), + name="keywordarticle", + unique_together=set([("keyword", "article")]), ), # Add temporary relation to new through model migrations.RunPython(migrate_keywords, reverse_code=demigrate_keywords), # Replace old relation with the new one migrations.RemoveField( - model_name='article', - name='keywords', + model_name="article", + name="keywords", ), migrations.AddField( - model_name='article', - name='keywords', - field=models.ManyToManyField(blank=True, null=True, through='submission.KeywordArticle', to='submission.Keyword'), + model_name="article", + name="keywords", + field=models.ManyToManyField( + blank=True, + null=True, + through="submission.KeywordArticle", + to="submission.Keyword", + ), ), ] diff --git a/src/submission/migrations/0051_auto_20210222_1452.py b/src/submission/migrations/0051_auto_20210222_1452.py index e007aacca2..18c62621e1 100644 --- a/src/submission/migrations/0051_auto_20210222_1452.py +++ b/src/submission/migrations/0051_auto_20210222_1452.py @@ -7,15 +7,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0050_support_ordered_keywords'), + ("submission", "0050_support_ordered_keywords"), ] operations = [ migrations.AlterField( - model_name='article', - name='keywords', - field=core.model_utils.M2MOrderedThroughField(blank=True, null=True, through='submission.KeywordArticle', to='submission.Keyword'), + model_name="article", + name="keywords", + field=core.model_utils.M2MOrderedThroughField( + blank=True, + null=True, + through="submission.KeywordArticle", + to="submission.Keyword", + ), ), ] diff --git a/src/submission/migrations/0051_hvad_to_modeltranslations.py b/src/submission/migrations/0051_hvad_to_modeltranslations.py index fa08a8f0be..e74d81d092 100644 --- a/src/submission/migrations/0051_hvad_to_modeltranslations.py +++ b/src/submission/migrations/0051_hvad_to_modeltranslations.py @@ -6,88 +6,113 @@ def migrate_sections(apps, schema_editor): - Section = apps.get_model('submission', 'Section') - SectionTranslation = apps.get_model('submission', 'SectionTranslation') + Section = apps.get_model("submission", "Section") + SectionTranslation = apps.get_model("submission", "SectionTranslation") translations = SectionTranslation.objects.all() for translation in translations: section = Section.objects.get(pk=translation.master_id) - setattr(section, 'name_{}'.format(translation.language_code), translation.hvad_name) - setattr(section, 'plural_{}'.format(translation.language_code), translation.hvad_plural) + setattr( + section, "name_{}".format(translation.language_code), translation.hvad_name + ) + setattr( + section, + "plural_{}".format(translation.language_code), + translation.hvad_plural, + ) section.save() class Migration(migrations.Migration): - dependencies = [ - ('submission', '0050_support_ordered_keywords'), + ("submission", "0050_support_ordered_keywords"), ] operations = [ migrations.RenameField( - model_name='sectiontranslation', - old_name='name', - new_name='hvad_name', + model_name="sectiontranslation", + old_name="name", + new_name="hvad_name", ), migrations.RenameField( - model_name='sectiontranslation', - old_name='plural', - new_name='hvad_plural', + model_name="sectiontranslation", + old_name="plural", + new_name="hvad_plural", ), migrations.AddField( - model_name='section', - name='name', + model_name="section", + name="name", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='plural', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', - max_length=200, null=True), + model_name="section", + name="plural", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), migrations.AddField( - model_name='section', - name='name_cy', + model_name="section", + name="name_cy", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='name_de', + model_name="section", + name="name_de", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='name_en', + model_name="section", + name="name_en", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='name_fr', + model_name="section", + name="name_fr", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='plural_cy', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', - max_length=200, null=True), + model_name="section", + name="plural_cy", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), migrations.AddField( - model_name='section', - name='plural_de', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', - max_length=200, null=True), + model_name="section", + name="plural_de", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), migrations.AddField( - model_name='section', - name='plural_en', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', - max_length=200, null=True), + model_name="section", + name="plural_en", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), migrations.AddField( - model_name='section', - name='plural_fr', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', - max_length=200, null=True), + model_name="section", + name="plural_fr", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), ] diff --git a/src/submission/migrations/0052_auto_20210222_1845.py b/src/submission/migrations/0052_auto_20210222_1845.py index 7bb5b485e0..b1c69d280e 100644 --- a/src/submission/migrations/0052_auto_20210222_1845.py +++ b/src/submission/migrations/0052_auto_20210222_1845.py @@ -6,22 +6,27 @@ def migrate_sections(apps, schema_editor): - Section = apps.get_model('submission', 'Section') - SectionTranslation = apps.get_model('submission', 'SectionTranslation') + Section = apps.get_model("submission", "Section") + SectionTranslation = apps.get_model("submission", "SectionTranslation") translations = SectionTranslation.objects.all() for translation in translations: section = Section.objects.get(pk=translation.master_id) - setattr(section, 'name_{}'.format(translation.language_code), translation.hvad_name) - setattr(section, 'plural_{}'.format(translation.language_code), translation.hvad_plural) + setattr( + section, "name_{}".format(translation.language_code), translation.hvad_name + ) + setattr( + section, + "plural_{}".format(translation.language_code), + translation.hvad_plural, + ) section.save() class Migration(migrations.Migration): - dependencies = [ - ('submission', '0051_hvad_to_modeltranslations'), + ("submission", "0051_hvad_to_modeltranslations"), ] operations = [ diff --git a/src/submission/migrations/0052_auto_20210525_1743.py b/src/submission/migrations/0052_auto_20210525_1743.py index 4074aa3486..6299b394c2 100644 --- a/src/submission/migrations/0052_auto_20210525_1743.py +++ b/src/submission/migrations/0052_auto_20210525_1743.py @@ -6,20 +6,29 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0051_auto_20210222_1452'), + ("submission", "0051_auto_20210222_1452"), ] operations = [ migrations.AddField( - model_name='frozenauthor', - name='name_prefix', - field=models.CharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300, null=True), + model_name="frozenauthor", + name="name_prefix", + field=models.CharField( + blank=True, + help_text="Optional name prefix (e.g: Prof or Dr)", + max_length=300, + null=True, + ), ), migrations.AddField( - model_name='frozenauthor', - name='name_suffix', - field=models.CharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300, null=True), + model_name="frozenauthor", + name="name_suffix", + field=models.CharField( + blank=True, + help_text="Optional name suffix (e.g.: Jr or III)", + max_length=300, + null=True, + ), ), ] diff --git a/src/submission/migrations/0053_auto_20210222_1849.py b/src/submission/migrations/0053_auto_20210222_1849.py index f13c29f94f..89fd7032b0 100644 --- a/src/submission/migrations/0053_auto_20210222_1849.py +++ b/src/submission/migrations/0053_auto_20210222_1849.py @@ -6,26 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0052_auto_20210222_1845'), + ("submission", "0052_auto_20210222_1845"), ] operations = [ migrations.AlterUniqueTogether( - name='sectiontranslation', + name="sectiontranslation", unique_together=set([]), ), migrations.RemoveField( - model_name='sectiontranslation', - name='master', + model_name="sectiontranslation", + name="master", ), migrations.AlterModelManagers( - name='section', - managers=[ - ], + name="section", + managers=[], ), migrations.DeleteModel( - name='SectionTranslation', + name="SectionTranslation", ), ] diff --git a/src/submission/migrations/0054_merge_20210624_0923.py b/src/submission/migrations/0054_merge_20210624_0923.py index 07ae3f422d..71a75495c7 100644 --- a/src/submission/migrations/0054_merge_20210624_0923.py +++ b/src/submission/migrations/0054_merge_20210624_0923.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0052_auto_20210525_1743'), - ('submission', '0053_auto_20210222_1849'), + ("submission", "0052_auto_20210525_1743"), + ("submission", "0053_auto_20210222_1849"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0055_auto_20210629_0641.py b/src/submission/migrations/0055_auto_20210629_0641.py index c47c160c19..181d8cc6b5 100644 --- a/src/submission/migrations/0055_auto_20210629_0641.py +++ b/src/submission/migrations/0055_auto_20210629_0641.py @@ -6,20 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0054_merge_20210624_0923'), + ("submission", "0054_merge_20210624_0923"), ] operations = [ migrations.AddField( - model_name='section', - name='name_nl', + model_name="section", + name="name_nl", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='plural_nl', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', max_length=200, null=True), + model_name="section", + name="plural_nl", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), ] diff --git a/src/submission/migrations/0056_auto_20210803_0906.py b/src/submission/migrations/0056_auto_20210803_0906.py index 31eee14006..ac0513fa6d 100644 --- a/src/submission/migrations/0056_auto_20210803_0906.py +++ b/src/submission/migrations/0056_auto_20210803_0906.py @@ -8,67 +8,96 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0055_auto_20210629_0641'), + ("submission", "0055_auto_20210629_0641"), ] operations = [ migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), - ('objects', submission.models.ArticleManager()), + ("allarticles", django.db.models.manager.Manager()), + ("objects", submission.models.ArticleManager()), ], ), migrations.AddField( - model_name='article', - name='abstract_cy', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_cy", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='abstract_de', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_de", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='abstract_en', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_en", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='abstract_fr', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_fr", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='abstract_nl', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_nl", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='title_cy', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_cy", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AddField( - model_name='article', - name='title_de', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_de", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AddField( - model_name='article', - name='title_en', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AddField( - model_name='article', - name='title_fr', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_fr", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AddField( - model_name='article', - name='title_nl', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_nl", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), ] diff --git a/src/submission/migrations/0057_frozen_email_and_orcid.py b/src/submission/migrations/0057_frozen_email_and_orcid.py index 20aebfe8d1..1f94c6826c 100644 --- a/src/submission/migrations/0057_frozen_email_and_orcid.py +++ b/src/submission/migrations/0057_frozen_email_and_orcid.py @@ -8,27 +8,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0056_auto_20210803_0906'), + ("submission", "0056_auto_20210803_0906"), ] operations = [ migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), - ('objects', submission.models.ArticleManager()), + ("allarticles", django.db.models.manager.Manager()), + ("objects", submission.models.ArticleManager()), ], ), migrations.AddField( - model_name='frozenauthor', - name='frozen_email', - field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Author Email'), + model_name="frozenauthor", + name="frozen_email", + field=models.EmailField( + blank=True, max_length=254, null=True, verbose_name="Author Email" + ), ), migrations.AddField( - model_name='frozenauthor', - name='frozen_orcid', - field=models.CharField(blank=True, help_text='ORCiD to be displayed when no account is associated with this author. It should be introduced in code format (e.g: 0000-0000-0000-000X)', max_length=40, null=True, verbose_name='ORCiD'), + model_name="frozenauthor", + name="frozen_orcid", + field=models.CharField( + blank=True, + help_text="ORCiD to be displayed when no account is associated with this author. It should be introduced in code format (e.g: 0000-0000-0000-000X)", + max_length=40, + null=True, + verbose_name="ORCiD", + ), ), ] diff --git a/src/submission/migrations/0057_merge_20210811_1506.py b/src/submission/migrations/0057_merge_20210811_1506.py index c0a0a87ff2..ae1ed22d2a 100644 --- a/src/submission/migrations/0057_merge_20210811_1506.py +++ b/src/submission/migrations/0057_merge_20210811_1506.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0047_merge_20200626_1336'), - ('submission', '0056_auto_20210803_0906'), + ("submission", "0047_merge_20200626_1336"), + ("submission", "0056_auto_20210803_0906"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0057_unique_keywords.py b/src/submission/migrations/0057_unique_keywords.py index 65131fff95..44deee1100 100644 --- a/src/submission/migrations/0057_unique_keywords.py +++ b/src/submission/migrations/0057_unique_keywords.py @@ -8,13 +8,15 @@ def deduplicate_keywords(apps, schema_editor): - """Merges duplicated keyword models """ + """Merges duplicated keyword models""" Keyword = apps.get_model("submission", "Keyword") - to_fix = Keyword.objects.values( - "word" - ).annotate( - Count("id") - ).order_by().filter(id__count__gt=1).values("word") + to_fix = ( + Keyword.objects.values("word") + .annotate(Count("id")) + .order_by() + .filter(id__count__gt=1) + .values("word") + ) for word in to_fix: kws = Keyword.objects.filter(word=word["word"]) @@ -26,15 +28,13 @@ def deduplicate_keywords(apps, schema_editor): class Migration(migrations.Migration): atomic = False dependencies = [ - ('submission', '0056_auto_20210803_0906'), + ("submission", "0056_auto_20210803_0906"), ] operations = [ - migrations.RunPython( - deduplicate_keywords, migrations.RunPython.noop - ), + migrations.RunPython(deduplicate_keywords, migrations.RunPython.noop), migrations.AlterField( - model_name='keyword', - name='word', + model_name="keyword", + name="word", field=models.CharField(max_length=200, unique=True), ), ] diff --git a/src/submission/migrations/0058_auto_20210812_1254.py b/src/submission/migrations/0058_auto_20210812_1254.py index 69eb164923..de7f1dda1d 100644 --- a/src/submission/migrations/0058_auto_20210812_1254.py +++ b/src/submission/migrations/0058_auto_20210812_1254.py @@ -6,15 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0057_merge_20210811_1506'), + ("submission", "0057_merge_20210811_1506"), ] operations = [ migrations.AlterModelManagers( - name='article', - managers=[ - ], + name="article", + managers=[], ), ] diff --git a/src/submission/migrations/0058_merge_20210827_0849.py b/src/submission/migrations/0058_merge_20210827_0849.py index 477fd0c679..b0ca91767c 100644 --- a/src/submission/migrations/0058_merge_20210827_0849.py +++ b/src/submission/migrations/0058_merge_20210827_0849.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0057_unique_keywords'), - ('submission', '0057_frozen_email_and_orcid'), + ("submission", "0057_unique_keywords"), + ("submission", "0057_frozen_email_and_orcid"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0059_better_page_metadata.py b/src/submission/migrations/0059_better_page_metadata.py index 343d36f011..a3594a3f90 100644 --- a/src/submission/migrations/0059_better_page_metadata.py +++ b/src/submission/migrations/0059_better_page_metadata.py @@ -8,52 +8,69 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0058_merge_20210827_0849'), + ("submission", "0058_merge_20210827_0849"), ] operations = [ migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), - ('objects', submission.models.ArticleManager()), + ("allarticles", django.db.models.manager.Manager()), + ("objects", submission.models.ArticleManager()), ], ), migrations.AddField( - model_name='article', - name='article_number', - field=models.PositiveIntegerField(blank=True, help_text="Optional article number to be displayed on issue and article pages. Not to be confused with article ID.", null=True), + model_name="article", + name="article_number", + field=models.PositiveIntegerField( + blank=True, + help_text="Optional article number to be displayed on issue and article pages. Not to be confused with article ID.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='first_page', + model_name="article", + name="first_page", field=models.PositiveIntegerField(blank=True, null=True), ), migrations.AddField( - model_name='article', - name='last_page', + model_name="article", + name="last_page", field=models.PositiveIntegerField(blank=True, null=True), ), migrations.AddField( - model_name='article', - name='publisher_name', - field=models.CharField(blank=True, help_text='Name of the publisher who published this article Only relevant to migrated articles from a different publisher', max_length=999, null=True), + model_name="article", + name="publisher_name", + field=models.CharField( + blank=True, + help_text="Name of the publisher who published this article Only relevant to migrated articles from a different publisher", + max_length=999, + null=True, + ), ), migrations.AddField( - model_name='article', - name='rights', - field=models.TextField(blank=True, help_text='A custom statement on the usage rights for this article and associated materials, to be rendered in the article page', null=True), + model_name="article", + name="rights", + field=models.TextField( + blank=True, + help_text="A custom statement on the usage rights for this article and associated materials, to be rendered in the article page", + null=True, + ), ), migrations.AddField( - model_name='article', - name='total_pages', + model_name="article", + name="total_pages", field=models.PositiveIntegerField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='page_numbers', - field=models.CharField(blank=True, help_text="Custom page range. e.g.: 'I-VII' or 1-3,4-8", max_length=32, null=True), + model_name="article", + name="page_numbers", + field=models.CharField( + blank=True, + help_text="Custom page range. e.g.: 'I-VII' or 1-3,4-8", + max_length=32, + null=True, + ), ), ] diff --git a/src/submission/migrations/0060_auto_20210923_1545.py b/src/submission/migrations/0060_auto_20210923_1545.py index c2f7a7df58..38a1712249 100644 --- a/src/submission/migrations/0060_auto_20210923_1545.py +++ b/src/submission/migrations/0060_auto_20210923_1545.py @@ -8,15 +8,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0059_better_page_metadata'), + ("submission", "0059_better_page_metadata"), ] operations = [ migrations.AddField( - model_name='frozenauthor', - name='display_email', - field=models.BooleanField(default=False, help_text='If checked, this authors email address link will be displayed on the article page.'), + model_name="frozenauthor", + name="display_email", + field=models.BooleanField( + default=False, + help_text="If checked, this authors email address link will be displayed on the article page.", + ), ), ] diff --git a/src/submission/migrations/0061_auto_20210923_1615.py b/src/submission/migrations/0061_auto_20210923_1615.py index fd897792c2..2f169ee641 100644 --- a/src/submission/migrations/0061_auto_20210923_1615.py +++ b/src/submission/migrations/0061_auto_20210923_1615.py @@ -23,13 +23,10 @@ def setup_display_emails(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('submission', '0060_auto_20210923_1545'), + ("submission", "0060_auto_20210923_1545"), ] operations = [ - migrations.RunPython( - setup_display_emails, migrations.RunPython.noop - ), + migrations.RunPython(setup_display_emails, migrations.RunPython.noop), ] diff --git a/src/submission/migrations/0062_auto_20211013_1133.py b/src/submission/migrations/0062_auto_20211013_1133.py index 217cd3e605..778d6422a3 100644 --- a/src/submission/migrations/0062_auto_20211013_1133.py +++ b/src/submission/migrations/0062_auto_20211013_1133.py @@ -8,22 +8,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0061_auto_20210923_1615'), + ("submission", "0061_auto_20210923_1615"), ] operations = [ migrations.AlterModelManagers( - name='article', + name="article", managers=[ - ('allarticles', django.db.models.manager.Manager()), - ('objects', submission.models.ArticleManager()), + ("allarticles", django.db.models.manager.Manager()), + ("objects", submission.models.ArticleManager()), ], ), migrations.AddField( - model_name='article', - name='publication_title', - field=models.CharField(blank=True, help_text='Name of the publisher who published this article Only relevant to migrated articles from a different publisher', max_length=999, null=True), + model_name="article", + name="publication_title", + field=models.CharField( + blank=True, + help_text="Name of the publisher who published this article Only relevant to migrated articles from a different publisher", + max_length=999, + null=True, + ), ), ] diff --git a/src/submission/migrations/0062_merge_20211011_0906.py b/src/submission/migrations/0062_merge_20211011_0906.py index 18ba681261..26545e30ca 100644 --- a/src/submission/migrations/0062_merge_20211011_0906.py +++ b/src/submission/migrations/0062_merge_20211011_0906.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0058_auto_20210812_1254'), - ('submission', '0061_auto_20210923_1615'), + ("submission", "0058_auto_20210812_1254"), + ("submission", "0061_auto_20210923_1615"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0063_merge_20211207_1133.py b/src/submission/migrations/0063_merge_20211207_1133.py index a613fd5910..3e7341dccf 100644 --- a/src/submission/migrations/0063_merge_20211207_1133.py +++ b/src/submission/migrations/0063_merge_20211207_1133.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0062_auto_20211013_1133'), - ('submission', '0062_merge_20211011_0906'), + ("submission", "0062_auto_20211013_1133"), + ("submission", "0062_merge_20211011_0906"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0064_auto_20211215_1224.py b/src/submission/migrations/0064_auto_20211215_1224.py index 387d6999aa..4f54d06024 100644 --- a/src/submission/migrations/0064_auto_20211215_1224.py +++ b/src/submission/migrations/0064_auto_20211215_1224.py @@ -6,15 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0063_merge_20211207_1133'), + ("submission", "0063_merge_20211207_1133"), ] operations = [ migrations.AddField( - model_name='frozenauthor', - name='frozen_biography', - field=models.TextField(blank=True, null=True, verbose_name='Frozen Biography'), + model_name="frozenauthor", + name="frozen_biography", + field=models.TextField( + blank=True, null=True, verbose_name="Frozen Biography" + ), ), ] diff --git a/src/submission/migrations/0065_auto_20211221_1128.py b/src/submission/migrations/0065_auto_20211221_1128.py index 105a7260d9..712f66ce95 100644 --- a/src/submission/migrations/0065_auto_20211221_1128.py +++ b/src/submission/migrations/0065_auto_20211221_1128.py @@ -6,20 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0064_auto_20211215_1224'), + ("submission", "0064_auto_20211215_1224"), ] operations = [ migrations.AlterField( - model_name='frozenauthor', - name='frozen_biography', - field=models.TextField(blank=True, help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", null=True, verbose_name='Frozen Biography'), + model_name="frozenauthor", + name="frozen_biography", + field=models.TextField( + blank=True, + help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", + null=True, + verbose_name="Frozen Biography", + ), ), migrations.AlterModelManagers( - name='article', - managers=[ - ], + name="article", + managers=[], ), ] diff --git a/src/submission/migrations/0066_article_issn_override.py b/src/submission/migrations/0066_article_issn_override.py index c52f880a83..b1763ec6a1 100644 --- a/src/submission/migrations/0066_article_issn_override.py +++ b/src/submission/migrations/0066_article_issn_override.py @@ -6,15 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0065_auto_20211221_1128'), + ("submission", "0065_auto_20211221_1128"), ] operations = [ migrations.AddField( - model_name='article', - name='ISSN_override', - field=models.CharField(blank=True, help_text="Original ISSN of this article's journal when published Only relevant for back content published under a different title", max_length=999, null=True), + model_name="article", + name="ISSN_override", + field=models.CharField( + blank=True, + help_text="Original ISSN of this article's journal when published Only relevant for back content published under a different title", + max_length=999, + null=True, + ), ), ] diff --git a/src/submission/migrations/0066_auto_20220201_1042.py b/src/submission/migrations/0066_auto_20220201_1042.py index ddcbabda1f..eed3a03c95 100644 --- a/src/submission/migrations/0066_auto_20220201_1042.py +++ b/src/submission/migrations/0066_auto_20220201_1042.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0065_auto_20211221_1128'), + ("submission", "0065_auto_20211221_1128"), ] operations = [ migrations.AlterField( - model_name='frozenauthor', - name='institution', + model_name="frozenauthor", + name="institution", field=models.CharField(blank=True, max_length=1000, null=True), ), ] diff --git a/src/submission/migrations/0067_auto_20220321_1306.py b/src/submission/migrations/0067_auto_20220321_1306.py index 137577936d..acd34f8475 100644 --- a/src/submission/migrations/0067_auto_20220321_1306.py +++ b/src/submission/migrations/0067_auto_20220321_1306.py @@ -6,35 +6,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0066_article_issn_override'), + ("submission", "0066_article_issn_override"), ] operations = [ migrations.AddField( - model_name='article', - name='last_modified', + model_name="article", + name="last_modified", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='frozenauthor', - name='last_modified', + model_name="frozenauthor", + name="last_modified", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='licence', - name='last_modified', + model_name="licence", + name="last_modified", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='publishernote', - name='last_modified', + model_name="publishernote", + name="last_modified", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='section', - name='last_modified', + model_name="section", + name="last_modified", field=models.DateTimeField(auto_now=True), ), ] diff --git a/src/submission/migrations/0068_merge_20220427_1042.py b/src/submission/migrations/0068_merge_20220427_1042.py index a222b3b043..10a8333ef5 100644 --- a/src/submission/migrations/0068_merge_20220427_1042.py +++ b/src/submission/migrations/0068_merge_20220427_1042.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0067_auto_20220321_1306'), - ('submission', '0066_auto_20220201_1042'), + ("submission", "0067_auto_20220321_1306"), + ("submission", "0066_auto_20220201_1042"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0069_delete_blank_keywords.py b/src/submission/migrations/0069_delete_blank_keywords.py index e632666c11..0dbdd3859b 100644 --- a/src/submission/migrations/0069_delete_blank_keywords.py +++ b/src/submission/migrations/0069_delete_blank_keywords.py @@ -14,10 +14,8 @@ def delete_blank_keywords(apps, schema_editor): class Migration(migrations.Migration): atomic = False dependencies = [ - ('submission', '0068_merge_20220427_1042'), + ("submission", "0068_merge_20220427_1042"), ] operations = [ - migrations.RunPython( - delete_blank_keywords, migrations.RunPython.noop - ), + migrations.RunPython(delete_blank_keywords, migrations.RunPython.noop), ] diff --git a/src/submission/migrations/0070_auto_20230120_1546.py b/src/submission/migrations/0070_auto_20230120_1546.py index 11529104e6..3d5f8e3013 100644 --- a/src/submission/migrations/0070_auto_20230120_1546.py +++ b/src/submission/migrations/0070_auto_20230120_1546.py @@ -6,64 +6,114 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0078_auto_20230120_1546'), + ("core", "0078_auto_20230120_1546"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('press', '0027_auto_20220107_1219'), - ('journal', '0057_verbose_names_20230301_1914'), - ('submission', '0069_delete_blank_keywords'), + ("press", "0027_auto_20220107_1219"), + ("journal", "0057_verbose_names_20230301_1914"), + ("submission", "0069_delete_blank_keywords"), ] operations = [ migrations.AlterField( - model_name='article', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='journal.journal'), + model_name="article", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="journal.journal", + ), ), migrations.AlterField( - model_name='article', - name='preprint_journal_article', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.article'), + model_name="article", + name="preprint_journal_article", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.article", + ), ), migrations.AlterField( - model_name='field', - name='journal', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='journal.journal'), + model_name="field", + name="journal", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="journal.journal", + ), ), migrations.AlterField( - model_name='field', - name='press', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='press.press'), + model_name="field", + name="press", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="press.press", + ), ), migrations.AlterField( - model_name='frozenauthor', - name='author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="frozenauthor", + name="author", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='frozenauthor', - name='country', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.country'), + model_name="frozenauthor", + name="country", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.country", + ), ), migrations.AlterField( - model_name='note', - name='creator', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="note", + name="creator", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='publishernote', - name='creator', - field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="publishernote", + name="creator", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='submissionconfiguration', - name='default_license', - field=models.ForeignKey(blank=True, help_text='The default license applied when no option is presented', null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.licence'), + model_name="submissionconfiguration", + name="default_license", + field=models.ForeignKey( + blank=True, + help_text="The default license applied when no option is presented", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.licence", + ), ), migrations.AlterField( - model_name='submissionconfiguration', - name='default_section', - field=models.ForeignKey(blank=True, help_text='The default section of articles when no option is presented', null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.section'), + model_name="submissionconfiguration", + name="default_section", + field=models.ForeignKey( + blank=True, + help_text="The default section of articles when no option is presented", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="submission.section", + ), ), ] diff --git a/src/submission/migrations/0070_auto_20230208_1233.py b/src/submission/migrations/0070_auto_20230208_1233.py index b04d6c6f79..55a097641b 100644 --- a/src/submission/migrations/0070_auto_20230208_1233.py +++ b/src/submission/migrations/0070_auto_20230208_1233.py @@ -6,30 +6,40 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0069_delete_blank_keywords'), + ("submission", "0069_delete_blank_keywords"), ] operations = [ migrations.AddField( - model_name='article', - name='abstract_en_us', - field=models.TextField(blank=True, help_text='Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.', null=True), + model_name="article", + name="abstract_en_us", + field=models.TextField( + blank=True, + help_text="Please avoid pasting content from word processors as they can add unwanted styling to the abstract. You can retype the abstract here or copy and paste it into notepad/a plain text editor before pasting here.", + null=True, + ), ), migrations.AddField( - model_name='article', - name='title_en_us', - field=models.CharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en_us", + field=models.CharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AddField( - model_name='section', - name='name_en_us', + model_name="section", + name="name_en_us", field=models.CharField(max_length=200, null=True), ), migrations.AddField( - model_name='section', - name='plural_en_us', - field=models.CharField(blank=True, help_text='Pluralised name for the section (e.g: Article -> Articles)', max_length=200, null=True), + model_name="section", + name="plural_en_us", + field=models.CharField( + blank=True, + help_text="Pluralised name for the section (e.g: Article -> Articles)", + max_length=200, + null=True, + ), ), ] diff --git a/src/submission/migrations/0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233.py b/src/submission/migrations/0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233.py index 451c12169d..da8e206fb7 100644 --- a/src/submission/migrations/0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233.py +++ b/src/submission/migrations/0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0070_auto_20230120_1546'), - ('submission', '0070_auto_20230208_1233'), + ("submission", "0070_auto_20230120_1546"), + ("submission", "0070_auto_20230208_1233"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0072_auto_20230322_1540.py b/src/submission/migrations/0072_auto_20230322_1540.py index 97455d781c..d522fc7908 100644 --- a/src/submission/migrations/0072_auto_20230322_1540.py +++ b/src/submission/migrations/0072_auto_20230322_1540.py @@ -5,45 +5,72 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233'), + ("submission", "0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233"), ] operations = [ migrations.AlterField( - model_name='article', - name='abstract', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_cy', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_cy", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_de', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_de", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_en', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_en", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_en_us', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_en_us", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_fr', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_fr", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='abstract_nl', - field=django_bleach.models.BleachField(blank=True, help_text='Copying and pasting from word processors is supported.', null=True), + model_name="article", + name="abstract_nl", + field=django_bleach.models.BleachField( + blank=True, + help_text="Copying and pasting from word processors is supported.", + null=True, + ), ), ] diff --git a/src/submission/migrations/0072_auto_20230607_1512.py b/src/submission/migrations/0072_auto_20230607_1512.py index c7ae6995b1..074dd6fe22 100644 --- a/src/submission/migrations/0072_auto_20230607_1512.py +++ b/src/submission/migrations/0072_auto_20230607_1512.py @@ -4,15 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233'), + ("submission", "0071_merge_0070_auto_20230120_1546_0070_auto_20230208_1233"), ] operations = [ migrations.AddField( - model_name='article', - name='reviews_shared', - field=models.BooleanField(default=False, help_text='Marked true when an editor manually shares reviews with other reviewers using the decision helper page.'), + model_name="article", + name="reviews_shared", + field=models.BooleanField( + default=False, + help_text="Marked true when an editor manually shares reviews with other reviewers using the decision helper page.", + ), ), ] diff --git a/src/submission/migrations/0073_bleach_title_20230523_1804.py b/src/submission/migrations/0073_bleach_title_20230523_1804.py index 557ba4ffd9..1906042c4b 100644 --- a/src/submission/migrations/0073_bleach_title_20230523_1804.py +++ b/src/submission/migrations/0073_bleach_title_20230523_1804.py @@ -5,46 +5,59 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0072_auto_20230322_1540'), - ('submission', '0072_auto_20230607_1512'), + ("submission", "0072_auto_20230322_1540"), + ("submission", "0072_auto_20230607_1512"), ] operations = [ migrations.AlterField( - model_name='article', - name='title', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999), + model_name="article", + name="title", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999 + ), ), migrations.AlterField( - model_name='article', - name='title_cy', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_cy", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_de', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_de", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_en', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_en_us', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en_us", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_fr', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_fr", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_nl', - field=django_bleach.models.BleachField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_nl", + field=django_bleach.models.BleachField( + help_text="Your article title", max_length=999, null=True + ), ), ] diff --git a/src/submission/migrations/0074_auto_20240212_1537.py b/src/submission/migrations/0074_auto_20240212_1537.py index ed96718b55..44a52c2598 100644 --- a/src/submission/migrations/0074_auto_20240212_1537.py +++ b/src/submission/migrations/0074_auto_20240212_1537.py @@ -4,40 +4,69 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0073_bleach_title_20230523_1804'), + ("submission", "0073_bleach_title_20230523_1804"), ] operations = [ migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_cy', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_cy", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_de', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_de", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_en', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_en", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_en_us', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_en_us", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_fr', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_fr", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='submissionconfiguration', - name='submission_file_text_nl', - field=models.CharField(default='Manuscript File', help_text='During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.', max_length=255, null=True), + model_name="submissionconfiguration", + name="submission_file_text_nl", + field=models.CharField( + default="Manuscript File", + help_text="During submission the author will be asked to upload a filethat is considered the main text of the article. You can usethis field to change the label for that file in submission.", + max_length=255, + null=True, + ), ), ] diff --git a/src/submission/migrations/0075_auto_20240312_0922.py b/src/submission/migrations/0075_auto_20240312_0922.py index 221604f3f8..e9b824b5bf 100644 --- a/src/submission/migrations/0075_auto_20240312_0922.py +++ b/src/submission/migrations/0075_auto_20240312_0922.py @@ -5,135 +5,174 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0074_auto_20240212_1537'), + ("submission", "0074_auto_20240212_1537"), ] operations = [ migrations.AlterField( - model_name='article', - name='abstract', + model_name="article", + name="abstract", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_cy', + model_name="article", + name="abstract_cy", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_de', + model_name="article", + name="abstract_de", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_en', + model_name="article", + name="abstract_en", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_en_us', + model_name="article", + name="abstract_en_us", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_fr', + model_name="article", + name="abstract_fr", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='abstract_nl', + model_name="article", + name="abstract_nl", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='article', - name='article_agreement', - field=core.model_utils.JanewayBleachField(default=''), + model_name="article", + name="article_agreement", + field=core.model_utils.JanewayBleachField(default=""), ), migrations.AlterField( - model_name='article', - name='comments_editor', - field=core.model_utils.JanewayBleachField(blank=True, help_text="Add any comments you'd like the editor to consider here.", null=True, verbose_name='Comments to the Editor'), + model_name="article", + name="comments_editor", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Add any comments you'd like the editor to consider here.", + null=True, + verbose_name="Comments to the Editor", + ), ), migrations.AlterField( - model_name='article', - name='competing_interests', - field=core.model_utils.JanewayBleachField(blank=True, help_text='If you have any conflict of interests in the publication of this article please state them here.', null=True), + model_name="article", + name="competing_interests", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="If you have any conflict of interests in the publication of this article please state them here.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='custom_how_to_cite', - field=core.model_utils.JanewayBleachField(blank=True, help_text="Custom 'how to cite' text. To be used only if the block generated by Janeway is not suitable.", null=True), + model_name="article", + name="custom_how_to_cite", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="Custom 'how to cite' text. To be used only if the block generated by Janeway is not suitable.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='non_specialist_summary', - field=core.model_utils.JanewayBleachField(blank=True, help_text='A summary of the article for non specialists.', null=True), + model_name="article", + name="non_specialist_summary", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="A summary of the article for non specialists.", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='rights', - field=core.model_utils.JanewayBleachField(blank=True, help_text='A custom statement on the usage rights for this article and associated materials, to be rendered in the article page', null=True), + model_name="article", + name="rights", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="A custom statement on the usage rights for this article and associated materials, to be rendered in the article page", + null=True, + ), ), migrations.AlterField( - model_name='article', - name='title', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999), + model_name="article", + name="title", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999 + ), ), migrations.AlterField( - model_name='article', - name='title_cy', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_cy", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_de', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_de", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_en', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_en_us', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_en_us", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_fr', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_fr", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='article', - name='title_nl', - field=core.model_utils.JanewayBleachCharField(help_text='Your article title', max_length=999, null=True), + model_name="article", + name="title_nl", + field=core.model_utils.JanewayBleachCharField( + help_text="Your article title", max_length=999, null=True + ), ), migrations.AlterField( - model_name='fieldanswer', - name='answer', + model_name="fieldanswer", + name="answer", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='frozenauthor', - name='frozen_biography', - field=core.model_utils.JanewayBleachField(blank=True, help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", null=True, verbose_name='Frozen Biography'), + model_name="frozenauthor", + name="frozen_biography", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", + null=True, + verbose_name="Frozen Biography", + ), ), migrations.AlterField( - model_name='licence', - name='text', + model_name="licence", + name="text", field=core.model_utils.JanewayBleachField(blank=True, null=True), ), migrations.AlterField( - model_name='note', - name='text', + model_name="note", + name="text", field=core.model_utils.JanewayBleachField(), ), migrations.AlterField( - model_name='publishernote', - name='text', + model_name="publishernote", + name="text", field=core.model_utils.JanewayBleachField(max_length=4000), ), ] diff --git a/src/submission/migrations/0075_auto_20240320_1518.py b/src/submission/migrations/0075_auto_20240320_1518.py index dc5f8981e3..13c5f53358 100644 --- a/src/submission/migrations/0075_auto_20240320_1518.py +++ b/src/submission/migrations/0075_auto_20240320_1518.py @@ -4,20 +4,27 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0074_auto_20240212_1537'), + ("submission", "0074_auto_20240212_1537"), ] operations = [ migrations.AddField( - model_name='funder', - name='funding_statement', - field=models.TextField(blank=True, help_text='Additional information regarding this funding entry'), + model_name="funder", + name="funding_statement", + field=models.TextField( + blank=True, + help_text="Additional information regarding this funding entry", + ), ), migrations.AlterField( - model_name='funder', - name='fundref_id', - field=models.CharField(blank=True, help_text='Funder DOI (optional). Enter as a full Uniform Resource Identifier (URI), such as https://dx.doi.org/10.13039/501100021082', max_length=500, null=True), + model_name="funder", + name="fundref_id", + field=models.CharField( + blank=True, + help_text="Funder DOI (optional). Enter as a full Uniform Resource Identifier (URI), such as https://dx.doi.org/10.13039/501100021082", + max_length=500, + null=True, + ), ), ] diff --git a/src/submission/migrations/0076_alter_article_date_published.py b/src/submission/migrations/0076_alter_article_date_published.py index ffea5658e4..e3b5d47574 100644 --- a/src/submission/migrations/0076_alter_article_date_published.py +++ b/src/submission/migrations/0076_alter_article_date_published.py @@ -5,15 +5,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0075_auto_20240312_0922'), + ("submission", "0075_auto_20240312_0922"), ] operations = [ migrations.AlterField( - model_name='article', - name='date_published', + model_name="article", + name="date_published", field=core.model_utils.DateTimePickerModelField(blank=True, null=True), ), ] diff --git a/src/submission/migrations/0076_auto_20240402_1301.py b/src/submission/migrations/0076_auto_20240402_1301.py index f2cd73be5c..6b980f46ab 100644 --- a/src/submission/migrations/0076_auto_20240402_1301.py +++ b/src/submission/migrations/0076_auto_20240402_1301.py @@ -9,7 +9,7 @@ def migrate_relationship_to_fk(apps, schema_editor): - Article = apps.get_model('submission', 'Article') + Article = apps.get_model("submission", "Article") for article in Article.objects.all(): for funder in article.funders.all(): logger.debug(f"Adding FK to {funder.pk}") @@ -18,21 +18,26 @@ def migrate_relationship_to_fk(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('submission', '0075_auto_20240320_1518'), + ("submission", "0075_auto_20240320_1518"), ] operations = [ migrations.AddField( - model_name='funder', - name='article', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='submission.article'), + model_name="funder", + name="article", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="submission.article", + ), ), migrations.AlterField( - model_name='article', - name='funders', - field=models.ManyToManyField(blank=True, related_name='funders', to='submission.Funder'), + model_name="article", + name="funders", + field=models.ManyToManyField( + blank=True, related_name="funders", to="submission.Funder" + ), ), migrations.RunPython( migrate_relationship_to_fk, diff --git a/src/submission/migrations/0077_auto_20240402_1326.py b/src/submission/migrations/0077_auto_20240402_1326.py index e24d855707..54004d10e7 100644 --- a/src/submission/migrations/0077_auto_20240402_1326.py +++ b/src/submission/migrations/0077_auto_20240402_1326.py @@ -4,18 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0076_auto_20240402_1301'), + ("submission", "0076_auto_20240402_1301"), ] operations = [ migrations.RenameModel( - old_name='Funder', - new_name='ArticleFunding', + old_name="Funder", + new_name="ArticleFunding", ), migrations.RemoveField( - model_name='article', - name='funders', + model_name="article", + name="funders", ), ] diff --git a/src/submission/migrations/0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326.py b/src/submission/migrations/0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326.py index e57654ef4d..d1e975af8c 100644 --- a/src/submission/migrations/0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326.py +++ b/src/submission/migrations/0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0075_auto_20240312_0922'), - ('submission', '0077_auto_20240402_1326'), + ("submission", "0075_auto_20240312_0922"), + ("submission", "0077_auto_20240402_1326"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0079_frozen_author_null_to_string.py b/src/submission/migrations/0079_frozen_author_null_to_string.py index e43a164167..9af1147e3e 100644 --- a/src/submission/migrations/0079_frozen_author_null_to_string.py +++ b/src/submission/migrations/0079_frozen_author_null_to_string.py @@ -4,31 +4,29 @@ def migrate_null_values(apps, schema_editor): - model = apps.get_model('submission', 'FrozenAuthor') + model = apps.get_model("submission", "FrozenAuthor") fields = [ - 'department', - 'first_name', - 'frozen_biography', - 'frozen_email', - 'frozen_orcid', - 'institution', - 'last_name', - 'middle_name', - 'name_prefix', - 'name_suffix', + "department", + "first_name", + "frozen_biography", + "frozen_email", + "frozen_orcid", + "institution", + "last_name", + "middle_name", + "name_prefix", + "name_suffix", ] migration_utils.store_empty_strings(model, fields) class Migration(migrations.Migration): - dependencies = [ - ('submission', '0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326'), + ("submission", "0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326"), ] operations = [ migrations.RunPython( - migrate_null_values, - reverse_code=migrations.RunPython.noop + migrate_null_values, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/submission/migrations/0079_merge_20240602_1739.py b/src/submission/migrations/0079_merge_20240602_1739.py index f34dbd3478..d72342895c 100644 --- a/src/submission/migrations/0079_merge_20240602_1739.py +++ b/src/submission/migrations/0079_merge_20240602_1739.py @@ -4,11 +4,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0076_alter_article_date_published'), - ('submission', '0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326'), + ("submission", "0076_alter_article_date_published"), + ("submission", "0078_merge_0075_auto_20240312_0922_0077_auto_20240402_1326"), ] - operations = [ - ] + operations = [] diff --git a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py index d49a0fb616..c31cc4f1f0 100644 --- a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py +++ b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py @@ -6,61 +6,101 @@ class Migration(migrations.Migration): - dependencies = [ - ('submission', '0079_merge_20240602_1739'), - ('submission', '0079_frozen_author_null_to_string'), + ("submission", "0079_merge_20240602_1739"), + ("submission", "0079_frozen_author_null_to_string"), ] operations = [ migrations.AlterField( - model_name='frozenauthor', - name='department', - field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="department", + field=models.CharField( + blank=True, + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='first_name', - field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="first_name", + field=models.CharField( + blank=True, + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='frozen_biography', - field=core.model_utils.JanewayBleachField(blank=True, help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", verbose_name='Frozen Biography'), + model_name="frozenauthor", + name="frozen_biography", + field=core.model_utils.JanewayBleachField( + blank=True, + help_text="The author's biography at the time they published the linked article. For this article only, it overrides any main biography attached to the author's account. If Frozen Biography is left blank, any main biography for the account will be populated instead.", + verbose_name="Frozen Biography", + ), ), migrations.AlterField( - model_name='frozenauthor', - name='frozen_email', - field=models.EmailField(blank=True, max_length=254, verbose_name='Author Email'), + model_name="frozenauthor", + name="frozen_email", + field=models.EmailField( + blank=True, max_length=254, verbose_name="Author Email" + ), ), migrations.AlterField( - model_name='frozenauthor', - name='frozen_orcid', - field=models.CharField(blank=True, help_text='ORCiD to be displayed when no account is associated with this author. It should be introduced in code format (e.g: 0000-0000-0000-000X)', max_length=40, verbose_name='ORCiD'), + model_name="frozenauthor", + name="frozen_orcid", + field=models.CharField( + blank=True, + help_text="ORCiD to be displayed when no account is associated with this author. It should be introduced in code format (e.g: 0000-0000-0000-000X)", + max_length=40, + verbose_name="ORCiD", + ), ), migrations.AlterField( - model_name='frozenauthor', - name='institution', - field=models.CharField(blank=True, max_length=1000, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="institution", + field=models.CharField( + blank=True, + max_length=1000, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='last_name', - field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="last_name", + field=models.CharField( + blank=True, + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='middle_name', - field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="middle_name", + field=models.CharField( + blank=True, + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='name_prefix', - field=models.CharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="name_prefix", + field=models.CharField( + blank=True, + help_text="Optional name prefix (e.g: Prof or Dr)", + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), migrations.AlterField( - model_name='frozenauthor', - name='name_suffix', - field=models.CharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300, validators=[utils.forms.plain_text_validator]), + model_name="frozenauthor", + name="name_suffix", + field=models.CharField( + blank=True, + help_text="Optional name suffix (e.g.: Jr or III)", + max_length=300, + validators=[utils.forms.plain_text_validator], + ), ), ] diff --git a/src/submission/migrations/0081_auto_20240927_1021.py b/src/submission/migrations/0081_auto_20240927_1021.py index 64d9ab7b0a..dd18a7bdcf 100644 --- a/src/submission/migrations/0081_auto_20240927_1021.py +++ b/src/submission/migrations/0081_auto_20240927_1021.py @@ -10,15 +10,13 @@ def update_text_editor_comments(apps, schema_editor): - Article = apps.get_model('submission', 'Article') + Article = apps.get_model("submission", "Article") articles = Article.objects.all() for article in articles: if article.comments_editor: try: - forms.plain_text_validator( - article.comments_editor - ) + forms.plain_text_validator(article.comments_editor) article.comments_editor = linebreaksbr(article.comments_editor) except ValidationError: # these fields may have harmful content, clean it with bleach @@ -29,14 +27,12 @@ def update_text_editor_comments(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('submission', '0080_frozen_author_bleach_20240507_1350'), + ("submission", "0080_frozen_author_bleach_20240507_1350"), ] operations = [ migrations.RunPython( - update_text_editor_comments, - reverse_code=migrations.RunPython.noop + update_text_editor_comments, reverse_code=migrations.RunPython.noop ), ] diff --git a/src/submission/models.py b/src/submission/models.py index 6e2c822024..c24e281190 100755 --- a/src/submission/models.py +++ b/src/submission/models.py @@ -37,7 +37,7 @@ import swapper from core.file_system import JanewayFileSystemStorage -from core.model_utils import( +from core.model_utils import ( AbstractLastModifiedModel, DynamicChoiceField, BaseSearchManagerMixin, @@ -68,7 +68,7 @@ def article_media_upload(instance, filename): try: - filename = str(uuid.uuid4()) + '.' + str(filename.split('.')[1]) + filename = str(uuid.uuid4()) + "." + str(filename.split(".")[1]) except IndexError: filename = str(uuid.uuid4()) @@ -77,173 +77,525 @@ def article_media_upload(instance, filename): SALUTATION_CHOICES = [ - ('', '---'), - ('Dr', 'Dr'), - ('Prof', 'Prof'), - ('Miss', 'Miss'), - ('Ms', 'Ms'), - ('Mrs', 'Mrs'), - ('Mr', 'Mr'), + ("", "---"), + ("Dr", "Dr"), + ("Prof", "Prof"), + ("Miss", "Miss"), + ("Ms", "Ms"), + ("Mrs", "Mrs"), + ("Mr", "Mr"), ] LANGUAGE_CHOICES = ( - (u'eng', u'English'), (u'abk', u'Abkhazian'), (u'ace', u'Achinese'), (u'ach', u'Acoli'), (u'ada', u'Adangme'), - (u'ady', u'Adyghe; Adygei'), (u'aar', u'Afar'), (u'afh', u'Afrihili'), (u'afr', u'Afrikaans'), - (u'afa', u'Afro-Asiatic languages'), (u'ain', u'Ainu'), (u'aka', u'Akan'), (u'akk', u'Akkadian'), - (u'sqi', u'Albanian'), - (u'ale', u'Aleut'), (u'alg', u'Algonquian languages'), (u'tut', u'Altaic languages'), (u'amh', u'Amharic'), - (u'anp', u'Angika'), (u'apa', u'Apache languages'), (u'ara', u'Arabic'), (u'arg', u'Aragonese'), - (u'arp', u'Arapaho'), - (u'arw', u'Arawak'), (u'hye', u'Armenian'), (u'rup', u'Aromanian; Arumanian; Macedo-Romanian'), - (u'art', u'Artificial languages'), (u'asm', u'Assamese'), (u'ast', u'Asturian; Bable; Leonese; Asturleonese'), - (u'ath', u'Athapascan languages'), (u'aus', u'Australian languages'), (u'map', u'Austronesian languages'), - (u'ava', u'Avaric'), (u'ave', u'Avestan'), (u'awa', u'Awadhi'), (u'aym', u'Aymara'), (u'aze', u'Azerbaijani'), - (u'ban', u'Balinese'), (u'bat', u'Baltic languages'), (u'bal', u'Baluchi'), (u'bam', u'Bambara'), - (u'bai', u'Bamileke languages'), (u'bad', u'Banda languages'), (u'bnt', u'Bantu languages'), (u'bas', u'Basa'), - (u'bak', u'Bashkir'), (u'eus', u'Basque'), (u'btk', u'Batak languages'), (u'bej', u'Beja; Bedawiyet'), - (u'bel', u'Belarusian'), (u'bem', u'Bemba'), (u'ben', u'Bengali'), (u'ber', u'Berber languages'), - (u'bho', u'Bhojpuri'), - (u'bih', u'Bihari languages'), (u'bik', u'Bikol'), (u'bin', u'Bini; Edo'), (u'bis', u'Bislama'), - (u'byn', u'Blin; Bilin'), (u'zbl', u'Blissymbols; Blissymbolics; Bliss'), - (u'nob', u'Bokm\xe5l, Norwegian; Norwegian Bokm\xe5l'), (u'bos', u'Bosnian'), (u'bra', u'Braj'), - (u'bre', u'Breton'), - (u'bug', u'Buginese'), (u'bul', u'Bulgarian'), (u'bua', u'Buriat'), (u'mya', u'Burmese'), (u'cad', u'Caddo'), - (u'cat', u'Catalan; Valencian'), (u'cau', u'Caucasian languages'), (u'ceb', u'Cebuano'), - (u'cel', u'Celtic languages'), - (u'cai', u'Central American Indian languages'), (u'khm', u'Central Khmer'), (u'chg', u'Chagatai'), - (u'cmc', u'Chamic languages'), (u'cha', u'Chamorro'), (u'che', u'Chechen'), (u'chr', u'Cherokee'), - (u'chy', u'Cheyenne'), (u'chb', u'Chibcha'), (u'nya', u'Chichewa; Chewa; Nyanja'), (u'zho', u'Chinese'), - (u'chn', u'Chinook jargon'), (u'chp', u'Chipewyan; Dene Suline'), (u'cho', u'Choctaw'), - (u'chu', u'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic'), - (u'chk', u'Chuukese'), - (u'chv', u'Chuvash'), (u'nwc', u'Classical Newari; Old Newari; Classical Nepal Bhasa'), - (u'syc', u'Classical Syriac'), - (u'cop', u'Coptic'), (u'cor', u'Cornish'), (u'cos', u'Corsican'), (u'cre', u'Cree'), (u'mus', u'Creek'), - (u'crp', u'Creoles and pidgins'), (u'cpe', u'Creoles and pidgins, English based'), - (u'cpf', u'Creoles and pidgins, French-based'), (u'cpp', u'Creoles and pidgins, Portuguese-based'), - (u'crh', u'Crimean Tatar; Crimean Turkish'), (u'hrv', u'Croatian'), (u'cus', u'Cushitic languages'), - (u'ces', u'Czech'), - (u'dak', u'Dakota'), (u'dan', u'Danish'), (u'dar', u'Dargwa'), (u'del', u'Delaware'), (u'din', u'Dinka'), - (u'div', u'Divehi; Dhivehi; Maldivian'), (u'doi', u'Dogri'), (u'dgr', u'Dogrib'), (u'dra', u'Dravidian languages'), - (u'dua', u'Duala'), (u'dum', u'Dutch, Middle (ca. 1050-1350)'), (u'nld', u'Dutch; Flemish'), (u'dyu', u'Dyula'), - (u'dzo', u'Dzongkha'), (u'frs', u'Eastern Frisian'), (u'efi', u'Efik'), (u'egy', u'Egyptian (Ancient)'), - (u'eka', u'Ekajuk'), (u'elx', u'Elamite'), (u'enm', u'English, Middle (1100-1500)'), - (u'ang', u'English, Old (ca. 450-1100)'), (u'myv', u'Erzya'), (u'epo', u'Esperanto'), (u'est', u'Estonian'), - (u'ewe', u'Ewe'), (u'ewo', u'Ewondo'), (u'fan', u'Fang'), (u'fat', u'Fanti'), (u'fao', u'Faroese'), - (u'fij', u'Fijian'), - (u'fil', u'Filipino; Pilipino'), (u'fin', u'Finnish'), (u'fiu', u'Finno-Ugrian languages'), (u'fon', u'Fon'), - (u'fra', u'French'), (u'frm', u'French, Middle (ca. 1400-1600)'), (u'fro', u'French, Old (842-ca. 1400)'), - (u'fur', u'Friulian'), (u'ful', u'Fulah'), (u'gaa', u'Ga'), (u'gla', u'Gaelic; Scottish Gaelic'), - (u'car', u'Galibi Carib'), (u'glg', u'Galician'), (u'lug', u'Ganda'), (u'gay', u'Gayo'), (u'gba', u'Gbaya'), - (u'gez', u'Geez'), (u'kat', u'Georgian'), (u'deu', u'German'), (u'gmh', u'German, Middle High (ca. 1050-1500)'), - (u'goh', u'German, Old High (ca. 750-1050)'), (u'gem', u'Germanic languages'), (u'gil', u'Gilbertese'), - (u'gon', u'Gondi'), (u'gor', u'Gorontalo'), (u'got', u'Gothic'), (u'grb', u'Grebo'), - (u'grc', u'Greek, Ancient (to 1453)'), (u'ell', u'Greek, Modern (1453-)'), (u'grn', u'Guarani'), - (u'guj', u'Gujarati'), - (u'gwi', u"Gwich'in"), (u'hai', u'Haida'), (u'hat', u'Haitian; Haitian Creole'), (u'hau', u'Hausa'), - (u'haw', u'Hawaiian'), (u'heb', u'Hebrew'), (u'her', u'Herero'), (u'hil', u'Hiligaynon'), - (u'him', u'Himachali languages; Western Pahari languages'), (u'hin', u'Hindi'), (u'hmo', u'Hiri Motu'), - (u'hit', u'Hittite'), (u'hmn', u'Hmong; Mong'), (u'hun', u'Hungarian'), (u'hup', u'Hupa'), (u'iba', u'Iban'), - (u'isl', u'Icelandic'), (u'ido', u'Ido'), (u'ibo', u'Igbo'), (u'ijo', u'Ijo languages'), (u'ilo', u'Iloko'), - (u'smn', u'Inari Sami'), (u'inc', u'Indic languages'), (u'ine', u'Indo-European languages'), - (u'ind', u'Indonesian'), - (u'inh', u'Ingush'), (u'ina', u'Interlingua (International Auxiliary Language Association)'), - (u'ile', u'Interlingue; Occidental'), (u'iku', u'Inuktitut'), (u'ipk', u'Inupiaq'), (u'ira', u'Iranian languages'), - (u'gle', u'Irish'), (u'mga', u'Irish, Middle (900-1200)'), (u'sga', u'Irish, Old (to 900)'), - (u'iro', u'Iroquoian languages'), (u'ita', u'Italian'), (u'jpn', u'Japanese'), (u'jav', u'Javanese'), - (u'jrb', u'Judeo-Arabic'), (u'jpr', u'Judeo-Persian'), (u'kbd', u'Kabardian'), (u'kab', u'Kabyle'), - (u'kac', u'Kachin; Jingpho'), (u'kal', u'Kalaallisut; Greenlandic'), (u'xal', u'Kalmyk; Oirat'), (u'kam', u'Kamba'), - (u'kan', u'Kannada'), (u'kau', u'Kanuri'), (u'kaa', u'Kara-Kalpak'), (u'krc', u'Karachay-Balkar'), - (u'krl', u'Karelian'), (u'kar', u'Karen languages'), (u'kas', u'Kashmiri'), (u'csb', u'Kashubian'), - (u'kaw', u'Kawi'), - (u'kaz', u'Kazakh'), (u'kha', u'Khasi'), (u'khi', u'Khoisan languages'), (u'kho', u'Khotanese;Sakan'), - (u'kik', u'Kikuyu; Gikuyu'), (u'kmb', u'Kimbundu'), (u'kin', u'Kinyarwanda'), (u'kir', u'Kirghiz; Kyrgyz'), - (u'tlh', u'Klingon; tlhIngan-Hol'), (u'kom', u'Komi'), (u'kon', u'Kongo'), (u'kok', u'Konkani'), - (u'kor', u'Korean'), - (u'kos', u'Kosraean'), (u'kpe', u'Kpelle'), (u'kro', u'Kru languages'), (u'kua', u'Kuanyama; Kwanyama'), - (u'kum', u'Kumyk'), (u'kur', u'Kurdish'), (u'kru', u'Kurukh'), (u'kut', u'Kutenai'), (u'lad', u'Ladino'), - (u'lah', u'Lahnda'), (u'lam', u'Lamba'), (u'day', u'Land Dayak languages'), (u'lao', u'Lao'), (u'lat', u'Latin'), - (u'lav', u'Latvian'), (u'lez', u'Lezghian'), (u'lim', u'Limburgan; Limburger; Limburgish'), (u'lin', u'Lingala'), - (u'lit', u'Lithuanian'), (u'jbo', u'Lojban'), (u'nds', u'Low German; Low Saxon; German, Low; Saxon, Low'), - (u'dsb', u'Lower Sorbian'), (u'loz', u'Lozi'), (u'lub', u'Luba-Katanga'), (u'lua', u'Luba-Lulua'), - (u'lui', u'Luiseno'), - (u'smj', u'Lule Sami'), (u'lun', u'Lunda'), (u'luo', u'Luo (Kenya and Tanzania)'), (u'lus', u'Lushai'), - (u'ltz', u'Luxembourgish; Letzeburgesch'), (u'mkd', u'Macedonian'), (u'mad', u'Madurese'), (u'mag', u'Magahi'), - (u'mai', u'Maithili'), (u'mak', u'Makasar'), (u'mlg', u'Malagasy'), (u'msa', u'Malay'), (u'mal', u'Malayalam'), - (u'mlt', u'Maltese'), (u'mnc', u'Manchu'), (u'mdr', u'Mandar'), (u'man', u'Mandingo'), (u'mni', u'Manipuri'), - (u'mno', u'Manobo languages'), (u'glv', u'Manx'), (u'mri', u'Maori'), (u'arn', u'Mapudungun; Mapuche'), - (u'mar', u'Marathi'), (u'chm', u'Mari'), (u'mah', u'Marshallese'), (u'mwr', u'Marwari'), (u'mas', u'Masai'), - (u'myn', u'Mayan languages'), (u'men', u'Mende'), (u'mic', u"Mi'kmaq; Micmac"), (u'min', u'Minangkabau'), - (u'mwl', u'Mirandese'), (u'moh', u'Mohawk'), (u'mdf', u'Moksha'), (u'mol', u'Moldavian; Moldovan'), - (u'mkh', u'Mon-Khmer languages'), (u'lol', u'Mongo'), (u'mon', u'Mongolian'), (u'mos', u'Mossi'), - (u'mul', u'Multiple languages'), (u'mun', u'Munda languages'), (u'nqo', u"N'Ko"), (u'nah', u'Nahuatl languages'), - (u'nau', u'Nauru'), (u'nav', u'Navajo; Navaho'), (u'nde', u'Ndebele, North; North Ndebele'), - (u'nbl', u'Ndebele, South; South Ndebele'), (u'ndo', u'Ndonga'), (u'nap', u'Neapolitan'), - (u'new', u'Nepal Bhasa; Newari'), (u'nep', u'Nepali'), (u'nia', u'Nias'), (u'nic', u'Niger-Kordofanian languages'), - (u'ssa', u'Nilo-Saharan languages'), (u'niu', u'Niuean'), (u'zxx', u'No linguistic content; Not applicable'), - (u'nog', u'Nogai'), (u'non', u'Norse, Old'), (u'nai', u'North American Indian languages'), - (u'frr', u'Northern Frisian'), (u'sme', u'Northern Sami'), (u'nor', u'Norwegian'), - (u'nno', u'Norwegian Nynorsk; Nynorsk, Norwegian'), (u'nub', u'Nubian languages'), (u'nym', u'Nyamwezi'), - (u'nyn', u'Nyankole'), (u'nyo', u'Nyoro'), (u'nzi', u'Nzima'), (u'oci', u'Occitan (post 1500)'), - (u'arc', u'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)'), (u'oji', u'Ojibwa'), - (u'ori', u'Oriya'), - (u'orm', u'Oromo'), (u'osa', u'Osage'), (u'oss', u'Ossetian; Ossetic'), (u'oto', u'Otomian languages'), - (u'pal', u'Pahlavi'), (u'pau', u'Palauan'), (u'pli', u'Pali'), (u'pam', u'Pampanga; Kapampangan'), - (u'pag', u'Pangasinan'), (u'pan', u'Panjabi; Punjabi'), (u'pap', u'Papiamento'), (u'paa', u'Papuan languages'), - (u'nso', u'Pedi; Sepedi; Northern Sotho'), (u'fas', u'Persian'), (u'peo', u'Persian, Old (ca. 600-400 B.C.)'), - (u'phi', u'Philippine languages'), (u'phn', u'Phoenician'), (u'pon', u'Pohnpeian'), (u'pol', u'Polish'), - (u'por', u'Portuguese'), (u'pra', u'Prakrit languages'), - (u'pro', u'Proven\xe7al, Old (to 1500); Occitan, Old (to 1500)'), (u'pus', u'Pushto; Pashto'), (u'que', u'Quechua'), - (u'raj', u'Rajasthani'), (u'rap', u'Rapanui'), (u'rar', u'Rarotongan; Cook Islands Maori'), - (u'qaa-qtz', u'Reserved for local use'), (u'roa', u'Romance languages'), (u'ron', u'Romanian'), - (u'roh', u'Romansh'), - (u'rom', u'Romany'), (u'run', u'Rundi'), (u'rus', u'Russian'), (u'sal', u'Salishan languages'), - (u'sam', u'Samaritan Aramaic'), (u'smi', u'Sami languages'), (u'smo', u'Samoan'), (u'sad', u'Sandawe'), - (u'sag', u'Sango'), (u'san', u'Sanskrit'), (u'sat', u'Santali'), (u'srd', u'Sardinian'), (u'sas', u'Sasak'), - (u'sco', u'Scots'), (u'sel', u'Selkup'), (u'sem', u'Semitic languages'), (u'srp', u'Serbian'), (u'srr', u'Serer'), - (u'shn', u'Shan'), (u'sna', u'Shona'), (u'iii', u'Sichuan Yi; Nuosu'), (u'scn', u'Sicilian'), (u'sid', u'Sidamo'), - (u'sgn', u'Sign Languages'), (u'bla', u'Siksika'), (u'snd', u'Sindhi'), (u'sin', u'Sinhala; Sinhalese'), - (u'sit', u'Sino-Tibetan languages'), (u'sio', u'Siouan languages'), (u'sms', u'Skolt Sami'), - (u'den', u'Slave (Athapascan)'), (u'sla', u'Slavic languages'), (u'slk', u'Slovak'), (u'slv', u'Slovenian'), - (u'sog', u'Sogdian'), (u'som', u'Somali'), (u'son', u'Songhai languages'), (u'snk', u'Soninke'), - (u'wen', u'Sorbian languages'), (u'sot', u'Sotho, Southern'), (u'sai', u'South American Indian languages'), - (u'alt', u'Southern Altai'), (u'sma', u'Southern Sami'), (u'spa', u'Spanish; Castilian'), (u'srn', u'Sranan Tongo'), - (u'zgh', u'Standard Moroccan Tamazight'), (u'suk', u'Sukuma'), (u'sux', u'Sumerian'), (u'sun', u'Sundanese'), - (u'sus', u'Susu'), (u'swa', u'Swahili'), (u'ssw', u'Swati'), (u'swe', u'Swedish'), - (u'gsw', u'Swiss German; Alemannic; Alsatian'), (u'syr', u'Syriac'), (u'tgl', u'Tagalog'), (u'tah', u'Tahitian'), - (u'tai', u'Tai languages'), (u'tgk', u'Tajik'), (u'tmh', u'Tamashek'), (u'tam', u'Tamil'), (u'tat', u'Tatar'), - (u'tel', u'Telugu'), (u'ter', u'Tereno'), (u'tet', u'Tetum'), (u'tha', u'Thai'), (u'bod', u'Tibetan'), - (u'tig', u'Tigre'), (u'tir', u'Tigrinya'), (u'tem', u'Timne'), (u'tiv', u'Tiv'), (u'tli', u'Tlingit'), - (u'tpi', u'Tok Pisin'), (u'tkl', u'Tokelau'), (u'tog', u'Tonga (Nyasa)'), (u'ton', u'Tonga (Tonga Islands)'), - (u'tsi', u'Tsimshian'), (u'tso', u'Tsonga'), (u'tsn', u'Tswana'), (u'tum', u'Tumbuka'), (u'tup', u'Tupi languages'), - (u'tur', u'Turkish'), (u'ota', u'Turkish, Ottoman (1500-1928)'), (u'tuk', u'Turkmen'), (u'tvl', u'Tuvalu'), - (u'tyv', u'Tuvinian'), (u'twi', u'Twi'), (u'udm', u'Udmurt'), (u'uga', u'Ugaritic'), (u'uig', u'Uighur; Uyghur'), - (u'ukr', u'Ukrainian'), (u'umb', u'Umbundu'), (u'mis', u'Uncoded languages'), (u'und', u'Undetermined'), - (u'hsb', u'Upper Sorbian'), (u'urd', u'Urdu'), (u'uzb', u'Uzbek'), (u'vai', u'Vai'), (u'ven', u'Venda'), - (u'vie', u'Vietnamese'), (u'vol', u'Volap\xfck'), (u'vot', u'Votic'), (u'wak', u'Wakashan languages'), - (u'wln', u'Walloon'), (u'war', u'Waray'), (u'was', u'Washo'), (u'cym', u'Welsh'), (u'fry', u'Western Frisian'), - (u'wal', u'Wolaitta; Wolaytta'), (u'wol', u'Wolof'), (u'xho', u'Xhosa'), (u'sah', u'Yakut'), (u'yao', u'Yao'), - (u'yap', u'Yapese'), (u'yid', u'Yiddish'), (u'yor', u'Yoruba'), (u'ypk', u'Yupik languages'), - (u'znd', u'Zande languages'), (u'zap', u'Zapotec'), (u'zza', u'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'), - (u'zen', u'Zenaga'), (u'zha', u'Zhuang; Chuang'), (u'zul', u'Zulu'), (u'zun', u'Zuni')) - -STAGE_UNSUBMITTED = 'Unsubmitted' -STAGE_UNASSIGNED = 'Unassigned' -STAGE_ASSIGNED = 'Assigned' -STAGE_UNDER_REVIEW = 'Under Review' -STAGE_UNDER_REVISION = 'Under Revision' -STAGE_REJECTED = 'Rejected' -STAGE_ACCEPTED = 'Accepted' -STAGE_EDITOR_COPYEDITING = 'Editor Copyediting' -STAGE_AUTHOR_COPYEDITING = 'Author Copyediting' -STAGE_FINAL_COPYEDITING = 'Final Copyediting' -STAGE_TYPESETTING = 'Typesetting' -STAGE_PROOFING = 'Proofing' -STAGE_READY_FOR_PUBLICATION = 'pre_publication' -STAGE_PUBLISHED = 'Published' -STAGE_PREPRINT_REVIEW = 'preprint_review' -STAGE_PREPRINT_PUBLISHED = 'preprint_published' -STAGE_ARCHIVED = 'Archived' + ("eng", "English"), + ("abk", "Abkhazian"), + ("ace", "Achinese"), + ("ach", "Acoli"), + ("ada", "Adangme"), + ("ady", "Adyghe; Adygei"), + ("aar", "Afar"), + ("afh", "Afrihili"), + ("afr", "Afrikaans"), + ("afa", "Afro-Asiatic languages"), + ("ain", "Ainu"), + ("aka", "Akan"), + ("akk", "Akkadian"), + ("sqi", "Albanian"), + ("ale", "Aleut"), + ("alg", "Algonquian languages"), + ("tut", "Altaic languages"), + ("amh", "Amharic"), + ("anp", "Angika"), + ("apa", "Apache languages"), + ("ara", "Arabic"), + ("arg", "Aragonese"), + ("arp", "Arapaho"), + ("arw", "Arawak"), + ("hye", "Armenian"), + ("rup", "Aromanian; Arumanian; Macedo-Romanian"), + ("art", "Artificial languages"), + ("asm", "Assamese"), + ("ast", "Asturian; Bable; Leonese; Asturleonese"), + ("ath", "Athapascan languages"), + ("aus", "Australian languages"), + ("map", "Austronesian languages"), + ("ava", "Avaric"), + ("ave", "Avestan"), + ("awa", "Awadhi"), + ("aym", "Aymara"), + ("aze", "Azerbaijani"), + ("ban", "Balinese"), + ("bat", "Baltic languages"), + ("bal", "Baluchi"), + ("bam", "Bambara"), + ("bai", "Bamileke languages"), + ("bad", "Banda languages"), + ("bnt", "Bantu languages"), + ("bas", "Basa"), + ("bak", "Bashkir"), + ("eus", "Basque"), + ("btk", "Batak languages"), + ("bej", "Beja; Bedawiyet"), + ("bel", "Belarusian"), + ("bem", "Bemba"), + ("ben", "Bengali"), + ("ber", "Berber languages"), + ("bho", "Bhojpuri"), + ("bih", "Bihari languages"), + ("bik", "Bikol"), + ("bin", "Bini; Edo"), + ("bis", "Bislama"), + ("byn", "Blin; Bilin"), + ("zbl", "Blissymbols; Blissymbolics; Bliss"), + ("nob", "Bokm\xe5l, Norwegian; Norwegian Bokm\xe5l"), + ("bos", "Bosnian"), + ("bra", "Braj"), + ("bre", "Breton"), + ("bug", "Buginese"), + ("bul", "Bulgarian"), + ("bua", "Buriat"), + ("mya", "Burmese"), + ("cad", "Caddo"), + ("cat", "Catalan; Valencian"), + ("cau", "Caucasian languages"), + ("ceb", "Cebuano"), + ("cel", "Celtic languages"), + ("cai", "Central American Indian languages"), + ("khm", "Central Khmer"), + ("chg", "Chagatai"), + ("cmc", "Chamic languages"), + ("cha", "Chamorro"), + ("che", "Chechen"), + ("chr", "Cherokee"), + ("chy", "Cheyenne"), + ("chb", "Chibcha"), + ("nya", "Chichewa; Chewa; Nyanja"), + ("zho", "Chinese"), + ("chn", "Chinook jargon"), + ("chp", "Chipewyan; Dene Suline"), + ("cho", "Choctaw"), + ( + "chu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", + ), + ("chk", "Chuukese"), + ("chv", "Chuvash"), + ("nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa"), + ("syc", "Classical Syriac"), + ("cop", "Coptic"), + ("cor", "Cornish"), + ("cos", "Corsican"), + ("cre", "Cree"), + ("mus", "Creek"), + ("crp", "Creoles and pidgins"), + ("cpe", "Creoles and pidgins, English based"), + ("cpf", "Creoles and pidgins, French-based"), + ("cpp", "Creoles and pidgins, Portuguese-based"), + ("crh", "Crimean Tatar; Crimean Turkish"), + ("hrv", "Croatian"), + ("cus", "Cushitic languages"), + ("ces", "Czech"), + ("dak", "Dakota"), + ("dan", "Danish"), + ("dar", "Dargwa"), + ("del", "Delaware"), + ("din", "Dinka"), + ("div", "Divehi; Dhivehi; Maldivian"), + ("doi", "Dogri"), + ("dgr", "Dogrib"), + ("dra", "Dravidian languages"), + ("dua", "Duala"), + ("dum", "Dutch, Middle (ca. 1050-1350)"), + ("nld", "Dutch; Flemish"), + ("dyu", "Dyula"), + ("dzo", "Dzongkha"), + ("frs", "Eastern Frisian"), + ("efi", "Efik"), + ("egy", "Egyptian (Ancient)"), + ("eka", "Ekajuk"), + ("elx", "Elamite"), + ("enm", "English, Middle (1100-1500)"), + ("ang", "English, Old (ca. 450-1100)"), + ("myv", "Erzya"), + ("epo", "Esperanto"), + ("est", "Estonian"), + ("ewe", "Ewe"), + ("ewo", "Ewondo"), + ("fan", "Fang"), + ("fat", "Fanti"), + ("fao", "Faroese"), + ("fij", "Fijian"), + ("fil", "Filipino; Pilipino"), + ("fin", "Finnish"), + ("fiu", "Finno-Ugrian languages"), + ("fon", "Fon"), + ("fra", "French"), + ("frm", "French, Middle (ca. 1400-1600)"), + ("fro", "French, Old (842-ca. 1400)"), + ("fur", "Friulian"), + ("ful", "Fulah"), + ("gaa", "Ga"), + ("gla", "Gaelic; Scottish Gaelic"), + ("car", "Galibi Carib"), + ("glg", "Galician"), + ("lug", "Ganda"), + ("gay", "Gayo"), + ("gba", "Gbaya"), + ("gez", "Geez"), + ("kat", "Georgian"), + ("deu", "German"), + ("gmh", "German, Middle High (ca. 1050-1500)"), + ("goh", "German, Old High (ca. 750-1050)"), + ("gem", "Germanic languages"), + ("gil", "Gilbertese"), + ("gon", "Gondi"), + ("gor", "Gorontalo"), + ("got", "Gothic"), + ("grb", "Grebo"), + ("grc", "Greek, Ancient (to 1453)"), + ("ell", "Greek, Modern (1453-)"), + ("grn", "Guarani"), + ("guj", "Gujarati"), + ("gwi", "Gwich'in"), + ("hai", "Haida"), + ("hat", "Haitian; Haitian Creole"), + ("hau", "Hausa"), + ("haw", "Hawaiian"), + ("heb", "Hebrew"), + ("her", "Herero"), + ("hil", "Hiligaynon"), + ("him", "Himachali languages; Western Pahari languages"), + ("hin", "Hindi"), + ("hmo", "Hiri Motu"), + ("hit", "Hittite"), + ("hmn", "Hmong; Mong"), + ("hun", "Hungarian"), + ("hup", "Hupa"), + ("iba", "Iban"), + ("isl", "Icelandic"), + ("ido", "Ido"), + ("ibo", "Igbo"), + ("ijo", "Ijo languages"), + ("ilo", "Iloko"), + ("smn", "Inari Sami"), + ("inc", "Indic languages"), + ("ine", "Indo-European languages"), + ("ind", "Indonesian"), + ("inh", "Ingush"), + ("ina", "Interlingua (International Auxiliary Language Association)"), + ("ile", "Interlingue; Occidental"), + ("iku", "Inuktitut"), + ("ipk", "Inupiaq"), + ("ira", "Iranian languages"), + ("gle", "Irish"), + ("mga", "Irish, Middle (900-1200)"), + ("sga", "Irish, Old (to 900)"), + ("iro", "Iroquoian languages"), + ("ita", "Italian"), + ("jpn", "Japanese"), + ("jav", "Javanese"), + ("jrb", "Judeo-Arabic"), + ("jpr", "Judeo-Persian"), + ("kbd", "Kabardian"), + ("kab", "Kabyle"), + ("kac", "Kachin; Jingpho"), + ("kal", "Kalaallisut; Greenlandic"), + ("xal", "Kalmyk; Oirat"), + ("kam", "Kamba"), + ("kan", "Kannada"), + ("kau", "Kanuri"), + ("kaa", "Kara-Kalpak"), + ("krc", "Karachay-Balkar"), + ("krl", "Karelian"), + ("kar", "Karen languages"), + ("kas", "Kashmiri"), + ("csb", "Kashubian"), + ("kaw", "Kawi"), + ("kaz", "Kazakh"), + ("kha", "Khasi"), + ("khi", "Khoisan languages"), + ("kho", "Khotanese;Sakan"), + ("kik", "Kikuyu; Gikuyu"), + ("kmb", "Kimbundu"), + ("kin", "Kinyarwanda"), + ("kir", "Kirghiz; Kyrgyz"), + ("tlh", "Klingon; tlhIngan-Hol"), + ("kom", "Komi"), + ("kon", "Kongo"), + ("kok", "Konkani"), + ("kor", "Korean"), + ("kos", "Kosraean"), + ("kpe", "Kpelle"), + ("kro", "Kru languages"), + ("kua", "Kuanyama; Kwanyama"), + ("kum", "Kumyk"), + ("kur", "Kurdish"), + ("kru", "Kurukh"), + ("kut", "Kutenai"), + ("lad", "Ladino"), + ("lah", "Lahnda"), + ("lam", "Lamba"), + ("day", "Land Dayak languages"), + ("lao", "Lao"), + ("lat", "Latin"), + ("lav", "Latvian"), + ("lez", "Lezghian"), + ("lim", "Limburgan; Limburger; Limburgish"), + ("lin", "Lingala"), + ("lit", "Lithuanian"), + ("jbo", "Lojban"), + ("nds", "Low German; Low Saxon; German, Low; Saxon, Low"), + ("dsb", "Lower Sorbian"), + ("loz", "Lozi"), + ("lub", "Luba-Katanga"), + ("lua", "Luba-Lulua"), + ("lui", "Luiseno"), + ("smj", "Lule Sami"), + ("lun", "Lunda"), + ("luo", "Luo (Kenya and Tanzania)"), + ("lus", "Lushai"), + ("ltz", "Luxembourgish; Letzeburgesch"), + ("mkd", "Macedonian"), + ("mad", "Madurese"), + ("mag", "Magahi"), + ("mai", "Maithili"), + ("mak", "Makasar"), + ("mlg", "Malagasy"), + ("msa", "Malay"), + ("mal", "Malayalam"), + ("mlt", "Maltese"), + ("mnc", "Manchu"), + ("mdr", "Mandar"), + ("man", "Mandingo"), + ("mni", "Manipuri"), + ("mno", "Manobo languages"), + ("glv", "Manx"), + ("mri", "Maori"), + ("arn", "Mapudungun; Mapuche"), + ("mar", "Marathi"), + ("chm", "Mari"), + ("mah", "Marshallese"), + ("mwr", "Marwari"), + ("mas", "Masai"), + ("myn", "Mayan languages"), + ("men", "Mende"), + ("mic", "Mi'kmaq; Micmac"), + ("min", "Minangkabau"), + ("mwl", "Mirandese"), + ("moh", "Mohawk"), + ("mdf", "Moksha"), + ("mol", "Moldavian; Moldovan"), + ("mkh", "Mon-Khmer languages"), + ("lol", "Mongo"), + ("mon", "Mongolian"), + ("mos", "Mossi"), + ("mul", "Multiple languages"), + ("mun", "Munda languages"), + ("nqo", "N'Ko"), + ("nah", "Nahuatl languages"), + ("nau", "Nauru"), + ("nav", "Navajo; Navaho"), + ("nde", "Ndebele, North; North Ndebele"), + ("nbl", "Ndebele, South; South Ndebele"), + ("ndo", "Ndonga"), + ("nap", "Neapolitan"), + ("new", "Nepal Bhasa; Newari"), + ("nep", "Nepali"), + ("nia", "Nias"), + ("nic", "Niger-Kordofanian languages"), + ("ssa", "Nilo-Saharan languages"), + ("niu", "Niuean"), + ("zxx", "No linguistic content; Not applicable"), + ("nog", "Nogai"), + ("non", "Norse, Old"), + ("nai", "North American Indian languages"), + ("frr", "Northern Frisian"), + ("sme", "Northern Sami"), + ("nor", "Norwegian"), + ("nno", "Norwegian Nynorsk; Nynorsk, Norwegian"), + ("nub", "Nubian languages"), + ("nym", "Nyamwezi"), + ("nyn", "Nyankole"), + ("nyo", "Nyoro"), + ("nzi", "Nzima"), + ("oci", "Occitan (post 1500)"), + ("arc", "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)"), + ("oji", "Ojibwa"), + ("ori", "Oriya"), + ("orm", "Oromo"), + ("osa", "Osage"), + ("oss", "Ossetian; Ossetic"), + ("oto", "Otomian languages"), + ("pal", "Pahlavi"), + ("pau", "Palauan"), + ("pli", "Pali"), + ("pam", "Pampanga; Kapampangan"), + ("pag", "Pangasinan"), + ("pan", "Panjabi; Punjabi"), + ("pap", "Papiamento"), + ("paa", "Papuan languages"), + ("nso", "Pedi; Sepedi; Northern Sotho"), + ("fas", "Persian"), + ("peo", "Persian, Old (ca. 600-400 B.C.)"), + ("phi", "Philippine languages"), + ("phn", "Phoenician"), + ("pon", "Pohnpeian"), + ("pol", "Polish"), + ("por", "Portuguese"), + ("pra", "Prakrit languages"), + ("pro", "Proven\xe7al, Old (to 1500); Occitan, Old (to 1500)"), + ("pus", "Pushto; Pashto"), + ("que", "Quechua"), + ("raj", "Rajasthani"), + ("rap", "Rapanui"), + ("rar", "Rarotongan; Cook Islands Maori"), + ("qaa-qtz", "Reserved for local use"), + ("roa", "Romance languages"), + ("ron", "Romanian"), + ("roh", "Romansh"), + ("rom", "Romany"), + ("run", "Rundi"), + ("rus", "Russian"), + ("sal", "Salishan languages"), + ("sam", "Samaritan Aramaic"), + ("smi", "Sami languages"), + ("smo", "Samoan"), + ("sad", "Sandawe"), + ("sag", "Sango"), + ("san", "Sanskrit"), + ("sat", "Santali"), + ("srd", "Sardinian"), + ("sas", "Sasak"), + ("sco", "Scots"), + ("sel", "Selkup"), + ("sem", "Semitic languages"), + ("srp", "Serbian"), + ("srr", "Serer"), + ("shn", "Shan"), + ("sna", "Shona"), + ("iii", "Sichuan Yi; Nuosu"), + ("scn", "Sicilian"), + ("sid", "Sidamo"), + ("sgn", "Sign Languages"), + ("bla", "Siksika"), + ("snd", "Sindhi"), + ("sin", "Sinhala; Sinhalese"), + ("sit", "Sino-Tibetan languages"), + ("sio", "Siouan languages"), + ("sms", "Skolt Sami"), + ("den", "Slave (Athapascan)"), + ("sla", "Slavic languages"), + ("slk", "Slovak"), + ("slv", "Slovenian"), + ("sog", "Sogdian"), + ("som", "Somali"), + ("son", "Songhai languages"), + ("snk", "Soninke"), + ("wen", "Sorbian languages"), + ("sot", "Sotho, Southern"), + ("sai", "South American Indian languages"), + ("alt", "Southern Altai"), + ("sma", "Southern Sami"), + ("spa", "Spanish; Castilian"), + ("srn", "Sranan Tongo"), + ("zgh", "Standard Moroccan Tamazight"), + ("suk", "Sukuma"), + ("sux", "Sumerian"), + ("sun", "Sundanese"), + ("sus", "Susu"), + ("swa", "Swahili"), + ("ssw", "Swati"), + ("swe", "Swedish"), + ("gsw", "Swiss German; Alemannic; Alsatian"), + ("syr", "Syriac"), + ("tgl", "Tagalog"), + ("tah", "Tahitian"), + ("tai", "Tai languages"), + ("tgk", "Tajik"), + ("tmh", "Tamashek"), + ("tam", "Tamil"), + ("tat", "Tatar"), + ("tel", "Telugu"), + ("ter", "Tereno"), + ("tet", "Tetum"), + ("tha", "Thai"), + ("bod", "Tibetan"), + ("tig", "Tigre"), + ("tir", "Tigrinya"), + ("tem", "Timne"), + ("tiv", "Tiv"), + ("tli", "Tlingit"), + ("tpi", "Tok Pisin"), + ("tkl", "Tokelau"), + ("tog", "Tonga (Nyasa)"), + ("ton", "Tonga (Tonga Islands)"), + ("tsi", "Tsimshian"), + ("tso", "Tsonga"), + ("tsn", "Tswana"), + ("tum", "Tumbuka"), + ("tup", "Tupi languages"), + ("tur", "Turkish"), + ("ota", "Turkish, Ottoman (1500-1928)"), + ("tuk", "Turkmen"), + ("tvl", "Tuvalu"), + ("tyv", "Tuvinian"), + ("twi", "Twi"), + ("udm", "Udmurt"), + ("uga", "Ugaritic"), + ("uig", "Uighur; Uyghur"), + ("ukr", "Ukrainian"), + ("umb", "Umbundu"), + ("mis", "Uncoded languages"), + ("und", "Undetermined"), + ("hsb", "Upper Sorbian"), + ("urd", "Urdu"), + ("uzb", "Uzbek"), + ("vai", "Vai"), + ("ven", "Venda"), + ("vie", "Vietnamese"), + ("vol", "Volap\xfck"), + ("vot", "Votic"), + ("wak", "Wakashan languages"), + ("wln", "Walloon"), + ("war", "Waray"), + ("was", "Washo"), + ("cym", "Welsh"), + ("fry", "Western Frisian"), + ("wal", "Wolaitta; Wolaytta"), + ("wol", "Wolof"), + ("xho", "Xhosa"), + ("sah", "Yakut"), + ("yao", "Yao"), + ("yap", "Yapese"), + ("yid", "Yiddish"), + ("yor", "Yoruba"), + ("ypk", "Yupik languages"), + ("znd", "Zande languages"), + ("zap", "Zapotec"), + ("zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"), + ("zen", "Zenaga"), + ("zha", "Zhuang; Chuang"), + ("zul", "Zulu"), + ("zun", "Zuni"), +) + +STAGE_UNSUBMITTED = "Unsubmitted" +STAGE_UNASSIGNED = "Unassigned" +STAGE_ASSIGNED = "Assigned" +STAGE_UNDER_REVIEW = "Under Review" +STAGE_UNDER_REVISION = "Under Revision" +STAGE_REJECTED = "Rejected" +STAGE_ACCEPTED = "Accepted" +STAGE_EDITOR_COPYEDITING = "Editor Copyediting" +STAGE_AUTHOR_COPYEDITING = "Author Copyediting" +STAGE_FINAL_COPYEDITING = "Final Copyediting" +STAGE_TYPESETTING = "Typesetting" +STAGE_PROOFING = "Proofing" +STAGE_READY_FOR_PUBLICATION = "pre_publication" +STAGE_PUBLISHED = "Published" +STAGE_PREPRINT_REVIEW = "preprint_review" +STAGE_PREPRINT_PUBLISHED = "preprint_published" +STAGE_ARCHIVED = "Archived" NEW_ARTICLE_STAGES = { STAGE_UNSUBMITTED, @@ -265,11 +617,7 @@ def article_media_upload(instance, filename): } # Stages used to determine if a review assignment is open -REVIEW_ACCESSIBLE_STAGES = { - STAGE_ASSIGNED, - STAGE_UNDER_REVIEW, - STAGE_UNDER_REVISION -} +REVIEW_ACCESSIBLE_STAGES = {STAGE_ASSIGNED, STAGE_UNDER_REVIEW, STAGE_UNDER_REVISION} COPYEDITING_STAGES = { STAGE_EDITOR_COPYEDITING, @@ -277,10 +625,7 @@ def article_media_upload(instance, filename): STAGE_FINAL_COPYEDITING, } -PREPRINT_STAGES = { - STAGE_PREPRINT_REVIEW, - STAGE_PREPRINT_PUBLISHED -} +PREPRINT_STAGES = {STAGE_PREPRINT_REVIEW, STAGE_PREPRINT_PUBLISHED} PUBLISHED_STAGES = { STAGE_PUBLISHED, @@ -288,23 +633,23 @@ def article_media_upload(instance, filename): } STAGE_CHOICES = [ - (STAGE_UNSUBMITTED, 'Unsubmitted'), - (STAGE_UNASSIGNED, 'Unassigned'), - (STAGE_ASSIGNED, 'Assigned to Editor'), - (STAGE_UNDER_REVIEW, 'Peer Review'), - (STAGE_UNDER_REVISION, 'Revision'), - (STAGE_REJECTED, 'Rejected'), - (STAGE_ACCEPTED, 'Accepted'), - (STAGE_EDITOR_COPYEDITING, 'Editor Copyediting'), - (STAGE_AUTHOR_COPYEDITING, 'Author Copyediting'), - (STAGE_FINAL_COPYEDITING, 'Final Copyediting'), - (STAGE_TYPESETTING, 'Typesetting'), - (STAGE_PROOFING, 'Proofing'), - (STAGE_READY_FOR_PUBLICATION, 'Pre Publication'), - (STAGE_PUBLISHED, 'Published'), - (STAGE_PREPRINT_REVIEW, 'Preprint Review'), - (STAGE_PREPRINT_PUBLISHED, 'Preprint Published'), - (STAGE_ARCHIVED, 'Archived'), + (STAGE_UNSUBMITTED, "Unsubmitted"), + (STAGE_UNASSIGNED, "Unassigned"), + (STAGE_ASSIGNED, "Assigned to Editor"), + (STAGE_UNDER_REVIEW, "Peer Review"), + (STAGE_UNDER_REVISION, "Revision"), + (STAGE_REJECTED, "Rejected"), + (STAGE_ACCEPTED, "Accepted"), + (STAGE_EDITOR_COPYEDITING, "Editor Copyediting"), + (STAGE_AUTHOR_COPYEDITING, "Author Copyediting"), + (STAGE_FINAL_COPYEDITING, "Final Copyediting"), + (STAGE_TYPESETTING, "Typesetting"), + (STAGE_PROOFING, "Proofing"), + (STAGE_READY_FOR_PUBLICATION, "Pre Publication"), + (STAGE_PUBLISHED, "Published"), + (STAGE_PREPRINT_REVIEW, "Preprint Review"), + (STAGE_PREPRINT_PUBLISHED, "Preprint Published"), + (STAGE_ARCHIVED, "Archived"), ] PLUGIN_WORKFLOW_STAGES = [] @@ -312,10 +657,10 @@ def article_media_upload(instance, filename): class ArticleFunding(models.Model): class Meta: - ordering = ('name',) + ordering = ("name",) article = models.ForeignKey( - 'submission.Article', + "submission.Article", on_delete=models.CASCADE, null=True, ) @@ -323,15 +668,15 @@ class Meta: max_length=500, blank=False, null=False, - help_text='Funder name', + help_text="Funder name", ) fundref_id = models.CharField( max_length=500, blank=True, null=True, - help_text='Funder DOI (optional). Enter as a full Uniform ' - 'Resource Identifier (URI), such as ' - 'https://dx.doi.org/10.13039/501100021082', + help_text="Funder DOI (optional). Enter as a full Uniform " + "Resource Identifier (URI), such as " + "https://dx.doi.org/10.13039/501100021082", ) funding_id = models.CharField( max_length=500, @@ -340,8 +685,7 @@ class Meta: help_text="The grant ID (optional). Enter the ID by itself", ) funding_statement = models.TextField( - blank=True, - help_text=_("Additional information regarding this funding entry") + blank=True, help_text=_("Additional information regarding this funding entry") ) def __str__(self): @@ -350,7 +694,7 @@ def __str__(self): class ArticleStageLog(models.Model): article = models.ForeignKey( - 'Article', + "Article", on_delete=models.CASCADE, ) stage_from = models.CharField(max_length=200, blank=False, null=False) @@ -358,20 +702,22 @@ class ArticleStageLog(models.Model): date_time = models.DateTimeField(default=timezone.now) class Meta: - ordering = ('-date_time',) + ordering = ("-date_time",) def __str__(self): - return "Article {article_pk} from {stage_from} to {stage_to} at {date_time}".format(article_pk=self.article.pk, - stage_from=self.stage_from, - stage_to=self.stage_to, - date_time=self.date_time) + return "Article {article_pk} from {stage_from} to {stage_to} at {date_time}".format( + article_pk=self.article.pk, + stage_from=self.stage_from, + stage_to=self.stage_to, + date_time=self.date_time, + ) class PublisherNote(AbstractLastModifiedModel): text = JanewayBleachField(max_length=4000, blank=False, null=False) sequence = models.PositiveIntegerField(default=999) creator = models.ForeignKey( - 'core.Account', + "core.Account", default=None, null=True, on_delete=models.SET_NULL, @@ -379,7 +725,7 @@ class PublisherNote(AbstractLastModifiedModel): date_time = models.DateTimeField(auto_now_add=True) class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): return "{0}: {1}".format(self.creator.full_name(), self.date_time) @@ -405,7 +751,7 @@ class KeywordArticle(models.Model): class Meta: ordering = ["order"] - unique_together = ('keyword', 'article') + unique_together = ("keyword", "article") def __str__(self): return self.keyword.word @@ -443,27 +789,30 @@ def mysql_search(self, search_term, search_filters, sort=None, site=None): if not search_term or not any(search_filters.values()): return queryset querysets = [] - if search_filters.get('title'): + if search_filters.get("title"): + querysets.append(self.get_queryset().filter(title__search=search_term)) + if search_filters.get("authors"): + querysets.append( + self.get_queryset().filter(frozenauthor__first_name__search=search_term) + ) querysets.append( - self.get_queryset().filter(title__search=search_term)) - if search_filters.get('authors'): - querysets.append(self.get_queryset().filter( - frozenauthor__first_name__search=search_term)) - querysets.append(self.get_queryset().filter( - frozenauthor__last_name__search=search_term)) + self.get_queryset().filter(frozenauthor__last_name__search=search_term) + ) if search_filters.get("abstract"): + querysets.append(self.get_queryset().filter(abstract__search=search_term)) + if search_filters.get("keywords"): querysets.append( - self.get_queryset().filter(abstract__search=search_term)) - if search_filters.get('keywords'): - querysets.append(self.get_queryset().filter( - keywords__word__search=search_term)) + self.get_queryset().filter(keywords__word__search=search_term) + ) if search_filters.get("full_text"): - querysets.append(self.get_queryset().filter( - galley__file__text__contents__search=search_term)) + querysets.append( + self.get_queryset().filter( + galley__file__text__contents__search=search_term + ) + ) for search_queryset in querysets: queryset |= search_queryset - if sort in self.SORT_KEYS: queryset = queryset.order_by(sort) @@ -479,14 +828,12 @@ def postgres_search(self, search_term, search_filters, sort=None, site=None): ) if site: queryset = queryset.filter(journal=site) - lookups, annotations = self.build_postgres_lookups( - search_term, search_filters) + lookups, annotations = self.build_postgres_lookups(search_term, search_filters) if annotations: queryset = queryset.annotate(**annotations) if lookups: queryset = queryset.filter(**lookups) - if not sort or sort not in self.SORT_KEYS: sort = "-relevance" @@ -502,20 +849,18 @@ def postgres_search(self, search_term, search_filters, sort=None, site=None): if "relevance" in sort: # Relevance is not a field but an annotation return Article.objects.raw( - f"SELECT * from ({inner_sql}) AS search " - "ORDER BY relevance DESC" + f"SELECT * from ({inner_sql}) AS search " "ORDER BY relevance DESC" ) else: order_by_sql = self.build_order_by_sql(sort) return Article.objects.raw( - f"SELECT * from ({inner_sql}) AS search " - f"{order_by_sql}" + f"SELECT * from ({inner_sql}) AS search " f"{order_by_sql}" ) return queryset.order_by(sort) def build_order_by_sql(self, sort_key): - """ Compiles and returns the ORDER BY statement in sql for the sort_key + """Compiles and returns the ORDER BY statement in sql for the sort_key It sorts an empty queryset of this model first, delegating the translation of the sort_key into the correct column name. Then it invokes Django's compiler to generate equivalent ORDER BY statement @@ -528,11 +873,10 @@ def build_order_by_sql(self, sort_key): order_strings = [] for field in order_by: order_strings.append("%s %s" % get_order_dir(field, "ASC")) - return 'ORDER BY %s' % ', '.join(order_strings) - + return "ORDER BY %s" % ", ".join(order_strings) def build_postgres_lookups(self, search_term, search_filters): - """ Build the necessary lookup expressions based on the provided filters + """Build the necessary lookup expressions based on the provided filters Each Filter is provided an arbitrary weight: +---------------+---------+---------+ @@ -552,24 +896,24 @@ def build_postgres_lookups(self, search_term, search_filters): lookups = {} annotations = {"relevance": models.Value(1.0, models.FloatField())} vectors = [] - if search_filters.get('title'): - vectors.append(SearchVector('title', weight="A")) - if search_filters.get('keywords'): - vectors.append(SearchVector('keywords__word', weight="B")) - if search_filters.get('authors'): - vectors.append(SearchVector('frozenauthor__last_name', weight="B")) - vectors.append(SearchVector('frozenauthor__first_name', weight="B")) + if search_filters.get("title"): + vectors.append(SearchVector("title", weight="A")) + if search_filters.get("keywords"): + vectors.append(SearchVector("keywords__word", weight="B")) + if search_filters.get("authors"): + vectors.append(SearchVector("frozenauthor__last_name", weight="B")) + vectors.append(SearchVector("frozenauthor__first_name", weight="B")) if search_filters.get("abstract"): - vectors.append(SearchVector('abstract', weight="C")) + vectors.append(SearchVector("abstract", weight="C")) if search_filters.get("full_text"): FileTextModel = swapper.load_model("core", "FileText") field_type = FileTextModel._meta.get_field("contents") if isinstance(field_type, SearchVectorField): - vectors.append(model_utils.SearchVector( - 'galley__file__text__contents', weight="D")) + vectors.append( + model_utils.SearchVector("galley__file__text__contents", weight="D") + ) else: - vectors.append(SearchVector( - 'galley__file__text__contents', weight="D")) + vectors.append(SearchVector("galley__file__text__contents", weight="D")) if vectors: # Combine all vectors vector = vectors[0] @@ -582,9 +926,9 @@ def build_postgres_lookups(self, search_term, search_filters): # values can range between .01 and .1 lookups["relevance__gte"] = 0.01 - if search_filters.get('ORCID'): - lookups['frozenauthor__author__orcid'] = search_term - lookups['frozenauthor__frozen_orcid'] = search_term + if search_filters.get("ORCID"): + lookups["frozenauthor__author__orcid"] = search_term + lookups["frozenauthor__frozen_orcid"] = search_term return lookups, annotations @staticmethod @@ -596,96 +940,140 @@ def stringify_queryset(queryset): class ActiveArticleManager(models.Manager): def get_queryset(self): - return super(ActiveArticleManager, self).get_queryset().exclude( - stage__in=[STAGE_ARCHIVED, STAGE_REJECTED, STAGE_UNSUBMITTED], + return ( + super(ActiveArticleManager, self) + .get_queryset() + .exclude( + stage__in=[STAGE_ARCHIVED, STAGE_REJECTED, STAGE_UNSUBMITTED], + ) ) class Article(AbstractLastModifiedModel): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", blank=True, null=True, on_delete=models.SET_NULL, ) # Metadata owner = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) title = JanewayBleachCharField( max_length=999, - help_text=_('Your article title'), + help_text=_("Your article title"), ) subtitle = models.CharField( # Note: subtitle is deprecated as of version 1.4.2 max_length=999, blank=True, null=True, - help_text=_('Do not use--deprecated in version 1.4.1 and later.') + help_text=_("Do not use--deprecated in version 1.4.1 and later."), ) abstract = JanewayBleachField( blank=True, null=True, ) non_specialist_summary = JanewayBleachField( - blank=True, null=True, - help_text='A summary of the article for non specialists.' + blank=True, null=True, help_text="A summary of the article for non specialists." ) keywords = M2MOrderedThroughField( Keyword, - blank=True, null=True, through='submission.KeywordArticle', + blank=True, + null=True, + through="submission.KeywordArticle", + ) + language = models.CharField( + max_length=200, + blank=True, + null=True, + choices=LANGUAGE_CHOICES, + help_text=_("The primary language of the article"), + ) + section = models.ForeignKey( + "Section", blank=True, null=True, on_delete=models.SET_NULL + ) + license = models.ForeignKey( + "Licence", blank=True, null=True, on_delete=models.SET_NULL + ) + publisher_notes = models.ManyToManyField( + "PublisherNote", blank=True, null=True, related_name="publisher_notes" ) - language = models.CharField(max_length=200, blank=True, null=True, choices=LANGUAGE_CHOICES, - help_text=_('The primary language of the article')) - section = models.ForeignKey('Section', blank=True, null=True, on_delete=models.SET_NULL) - license = models.ForeignKey('Licence', blank=True, null=True, on_delete=models.SET_NULL) - publisher_notes = models.ManyToManyField('PublisherNote', blank=True, null=True, related_name='publisher_notes') # Remote: a flag that specifies that this article is actually a _link_ to a remote instance # this is useful for overlay journals. The ToC display of an issue uses this flag to link to a DOI rather # than an internal URL - is_remote = models.BooleanField(default=False, verbose_name="Remote article", - help_text="Check if this article is remote") - remote_url = models.URLField(blank=True, null=True, help_text="If the article is remote, its URL should be added.") + is_remote = models.BooleanField( + default=False, + verbose_name="Remote article", + help_text="Check if this article is remote", + ) + remote_url = models.URLField( + blank=True, + null=True, + help_text="If the article is remote, its URL should be added.", + ) # Author - authors = models.ManyToManyField('core.Account', blank=True, null=True, related_name='authors') - correspondence_author = models.ForeignKey('core.Account', related_name='correspondence_author', blank=True, - null=True, on_delete=models.SET_NULL) + authors = models.ManyToManyField( + "core.Account", blank=True, null=True, related_name="authors" + ) + correspondence_author = models.ForeignKey( + "core.Account", + related_name="correspondence_author", + blank=True, + null=True, + on_delete=models.SET_NULL, + ) competing_interests_bool = models.BooleanField(default=False) competing_interests = JanewayBleachField( - blank=True, null=True, + blank=True, + null=True, help_text="If you have any conflict " - "of interests in the publication of this " - "article please state them here.", + "of interests in the publication of this " + "article please state them here.", ) rights = JanewayBleachField( - blank=True, null=True, + blank=True, + null=True, help_text="A custom statement on the usage rights for this article" - " and associated materials, to be rendered in the article page" + " and associated materials, to be rendered in the article page", ) article_number = models.PositiveIntegerField( - blank=True, null=True, - help_text="Optional article number to be displayed on issue and article pages. Not to be confused with article ID." + blank=True, + null=True, + help_text="Optional article number to be displayed on issue and article pages. Not to be confused with article ID.", ) # Files - manuscript_files = models.ManyToManyField('core.File', null=True, blank=True, related_name='manuscript_files') - data_figure_files = models.ManyToManyField('core.File', null=True, blank=True, related_name='data_figure_files') - supplementary_files = models.ManyToManyField('core.SupplementaryFile', null=True, blank=True, related_name='supp') + manuscript_files = models.ManyToManyField( + "core.File", null=True, blank=True, related_name="manuscript_files" + ) + data_figure_files = models.ManyToManyField( + "core.File", null=True, blank=True, related_name="data_figure_files" + ) + supplementary_files = models.ManyToManyField( + "core.SupplementaryFile", null=True, blank=True, related_name="supp" + ) source_files = models.ManyToManyField( - 'core.File', + "core.File", blank=True, - related_name='source_files', + related_name="source_files", ) # Galley - render_galley = models.ForeignKey('core.Galley', related_name='render_galley', blank=True, null=True, - on_delete=models.SET_NULL) + render_galley = models.ForeignKey( + "core.Galley", + related_name="render_galley", + blank=True, + null=True, + on_delete=models.SET_NULL, + ) # Dates date_started = models.DateTimeField(auto_now_add=True) @@ -701,7 +1089,9 @@ class Article(AbstractLastModifiedModel): first_page = models.PositiveIntegerField(blank=True, null=True) last_page = models.PositiveIntegerField(blank=True, null=True) page_numbers = models.CharField( - max_length=32, blank=True, null=True, + max_length=32, + blank=True, + null=True, help_text=_("Custom page range. e.g.: 'I-VII' or 1-3,4-8"), ) total_pages = models.PositiveIntegerField(blank=True, null=True) @@ -724,16 +1114,30 @@ class Article(AbstractLastModifiedModel): publication_fees = models.BooleanField(default=False) submission_requirements = models.BooleanField(default=False) copyright_notice = models.BooleanField(default=False) - comments_editor = JanewayBleachField(blank=True, null=True, verbose_name="Comments to the Editor", - help_text=_("Add any comments you'd like the editor to consider here.")) + comments_editor = JanewayBleachField( + blank=True, + null=True, + verbose_name="Comments to the Editor", + help_text=_("Add any comments you'd like the editor to consider here."), + ) # an image of recommended size: 750 x 324 - large_image_file = models.ForeignKey('core.File', null=True, blank=True, related_name='image_file', - on_delete=models.SET_NULL) + large_image_file = models.ForeignKey( + "core.File", + null=True, + blank=True, + related_name="image_file", + on_delete=models.SET_NULL, + ) exclude_from_slider = models.BooleanField(default=False) - thumbnail_image_file = models.ForeignKey('core.File', null=True, blank=True, related_name='thumbnail_file', - on_delete=models.SET_NULL) + thumbnail_image_file = models.ForeignKey( + "core.File", + null=True, + blank=True, + related_name="thumbnail_file", + on_delete=models.SET_NULL, + ) # Whether or not we should display that this article has been "peer reviewed" peer_reviewed = models.BooleanField(default=True) @@ -744,32 +1148,44 @@ class Article(AbstractLastModifiedModel): metric_stats = None # Agreement, this field records the submission checklist that was present when this article was submitted. - article_agreement = JanewayBleachField(default='') + article_agreement = JanewayBleachField(default="") custom_how_to_cite = JanewayBleachField( - blank=True, null=True, - help_text=_("Custom 'how to cite' text. To be used only if the block" - " generated by Janeway is not suitable."), + blank=True, + null=True, + help_text=_( + "Custom 'how to cite' text. To be used only if the block" + " generated by Janeway is not suitable." + ), ) publisher_name = models.CharField( - max_length=999, null=True, blank=True, - help_text=_("Name of the publisher who published this article" + max_length=999, + null=True, + blank=True, + help_text=_( + "Name of the publisher who published this article" " Only relevant to migrated articles from a different publisher" - ) + ), ) publication_title = models.CharField( - max_length=999, null=True, blank=True, - help_text=_("Name of the publisher who published this article" + max_length=999, + null=True, + blank=True, + help_text=_( + "Name of the publisher who published this article" " Only relevant to migrated articles from a different publisher" - ) + ), ) ISSN_override = models.CharField( - max_length=999, null=True, blank=True, - help_text=_("Original ISSN of this article's journal when published" + max_length=999, + null=True, + blank=True, + help_text=_( + "Original ISSN of this article's journal when published" " Only relevant for back content published under a different title" - ) + ), ) # iThenticate ID @@ -778,32 +1194,34 @@ class Article(AbstractLastModifiedModel): # Primary issue, allows the Editor to set the Submission's primary Issue primary_issue = models.ForeignKey( - 'journal.Issue', + "journal.Issue", blank=True, null=True, on_delete=models.SET_NULL, - help_text='You can only assign an issue ' - 'that this article is part of. ' - 'You can add this article to an ' - 'issue from the Issue Manager.', + help_text="You can only assign an issue " + "that this article is part of. " + "You can add this article to an " + "issue from the Issue Manager.", ) projected_issue = models.ForeignKey( - 'journal.Issue', + "journal.Issue", blank=True, null=True, on_delete=models.SET_NULL, - related_name='projected_issue', - help_text='This field is for internal purposes only ' - 'before publication. You can use it to ' - 'track likely issue assignment before formally ' - 'assigning an issue.', + related_name="projected_issue", + help_text="This field is for internal purposes only " + "before publication. You can use it to " + "track likely issue assignment before formally " + "assigning an issue.", ) # Meta - meta_image = models.ImageField(blank=True, null=True, upload_to=article_media_upload, storage=fs) + meta_image = models.ImageField( + blank=True, null=True, upload_to=article_media_upload, storage=fs + ) preprint_journal_article = models.ForeignKey( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.SET_NULL, @@ -812,14 +1230,14 @@ class Article(AbstractLastModifiedModel): reviews_shared = models.BooleanField( default=False, help_text="Marked true when an editor manually shares reviews with " - "other reviewers using the decision helper page.", + "other reviewers using the decision helper page.", ) objects = ArticleSearchManager() active_objects = ActiveArticleManager() class Meta: - ordering = ('-date_published', 'title') + ordering = ("-date_published", "title") @property def safe_title(self): @@ -835,7 +1253,7 @@ def how_to_cite(self): template = "common/elements/how_to_cite.html" authors = self.frozenauthor_set.all() - author_str = '' + author_str = "" for author in authors: if author == authors.first(): author_str = author.citation_name() @@ -848,9 +1266,7 @@ def how_to_cite(self): year_str = "" if self.date_published: year_str = "({:%Y})".format(self.date_published) - journal_str = "%s" % ( - self.publication_title or self.journal.name - ) + journal_str = "%s" % (self.publication_title or self.journal.name) issue_str = "" issue = self.issue if issue: @@ -860,7 +1276,7 @@ def how_to_cite(self): else: issue_str = str(issue.volume) elif issue.issue and issue.issue != "0": - issue_str = str(issue.issue) + issue_str = str(issue.issue) if self.article_number: issue_str += ": {}".format(self.article_number) @@ -870,8 +1286,11 @@ def how_to_cite(self): pages_str = " {0}.".format(self.page_range) doi = self.get_doi() if doi: - doi_str = ('doi: ' - 'https://doi.org/{0}'.format(doi)) + doi_str = ( + 'doi: ' "https://doi.org/{0}".format( + doi + ) + ) context = { "author_str": author_str, @@ -910,20 +1329,22 @@ def has_galley(self): @cache(600) def publication_detail_settings(journal): display_date_accepted = journal.get_setting( - group_name='article', - setting_name='display_date_accepted', + group_name="article", + setting_name="display_date_accepted", ) display_date_submitted = journal.get_setting( - group_name='article', - setting_name='display_date_submitted', + group_name="article", + setting_name="display_date_submitted", ) return display_date_submitted, display_date_accepted @property def has_publication_details(self): """Determines if an article has publication details override""" - display_date_submitted, display_date_accepted = self.publication_detail_settings(self.journal) - return( + display_date_submitted, display_date_accepted = ( + self.publication_detail_settings(self.journal) + ) + return ( self.page_range or self.article_number or self.publisher_name @@ -943,14 +1364,12 @@ def journal_issn(self): @property def publisher(self): - return ( - self.publisher_name - or self.journal.publisher - or self.journal.press.name - ) + return self.publisher_name or self.journal.publisher or self.journal.press.name def journal_sections(self): - return ((section.id, section.name) for section in self.journal.section_set.all()) + return ( + (section.id, section.name) for section in self.journal.section_set.all() + ) @property def carousel_subtitle(self): @@ -961,10 +1380,12 @@ def carousel_subtitle(self): for author in self.frozenauthor_set.all(): if idx > 0: idx = 1 - carousel_text += ', ' + carousel_text += ", " if author.institution: - carousel_text += author.full_name() + " ({0})".format(author.institution) + carousel_text += author.full_name() + " ({0})".format( + author.institution + ) else: carousel_text += author.full_name() @@ -978,7 +1399,7 @@ def carousel_title(self): @property def carousel_image_resolver(self): - return 'article_file_download' + return "article_file_download" @property def pdfs(self): @@ -1004,7 +1425,8 @@ def get_render_galley(self): @property def xml_galleys(self): ret = self.galley_set.filter(file__mime_type__in=files.XML_MIMETYPES).order_by( - "sequence") + "sequence" + ) return ret @@ -1025,16 +1447,12 @@ def all_galley_file_pks(self): @property def has_all_supplements(self): - for xml_file in self.xml_galleys: xml_file_contents = xml_file.get_file(self) - souped_xml = BeautifulSoup(xml_file_contents, 'lxml') + souped_xml = BeautifulSoup(xml_file_contents, "lxml") - elements = { - 'img': 'src', - 'graphic': 'xlink:href' - } + elements = {"img": "src", "graphic": "xlink:href"} # iterate over all found elements for element, attribute in elements.items(): @@ -1049,7 +1467,9 @@ def has_all_supplements(self): return False try: - named_files = self.data_figure_files.filter(original_filename=url).first() + named_files = self.data_figure_files.filter( + original_filename=url + ).first() if not named_files: return False @@ -1060,7 +1480,7 @@ def has_all_supplements(self): return True def index_full_text(self): - """ Indexes the render galley for full text search + """Indexes the render galley for full text search :return: A boolean indicating if a file has been processed """ indexed = False @@ -1082,24 +1502,32 @@ def index_full_text(self): break return indexed - @property @cache(300) def identifier(self): from identifiers import models as identifier_models + try: type_to_fetch = "doi" - return identifier_models.Identifier.objects.filter(id_type=type_to_fetch, article=self)[0] + return identifier_models.Identifier.objects.filter( + id_type=type_to_fetch, article=self + )[0] except BaseException: - new_id = identifier_models.Identifier(id_type="id", identifier=self.id, article=self) + new_id = identifier_models.Identifier( + id_type="id", identifier=self.id, article=self + ) return new_id def get_identifier(self, identifier_type, object=False): try: try: - doi = identifier_models.Identifier.objects.get(id_type=identifier_type, article=self) + doi = identifier_models.Identifier.objects.get( + id_type=identifier_type, article=self + ) except identifier_models.Identifier.MultipleObjectsReturned: - doi = identifier_models.Identifier.objects.filter(id_type=identifier_type, article=self)[0] + doi = identifier_models.Identifier.objects.filter( + id_type=identifier_type, article=self + )[0] if not object: return doi.identifier else: @@ -1108,17 +1536,17 @@ def get_identifier(self, identifier_type, object=False): return None def get_doi(self): - return self.get_identifier('doi') + return self.get_identifier("doi") def get_doi_url(self): - ident = self.get_identifier('doi', object=True) + ident = self.get_identifier("doi", object=True) if ident: return ident.get_doi_url() return None @property def get_doi_object(self): - return self.get_identifier('doi', object=True) + return self.get_identifier("doi", object=True) @property @cache(30) @@ -1128,10 +1556,11 @@ def doi_pattern_preview(self): @property def identifiers(self): from identifiers import models as identifier_models + return identifier_models.Identifier.objects.filter(article=self) def get_pubid(self): - return self.get_identifier('pubid') + return self.get_identifier("pubid") def non_correspondence_authors(self): if self.correspondence_author: @@ -1152,7 +1581,10 @@ def is_accepted(self): if self.stage == STAGE_ACCEPTED: return True - if self.stage not in NEW_ARTICLE_STAGES | REVIEW_STAGES and self.stage != STAGE_REJECTED: + if ( + self.stage not in NEW_ARTICLE_STAGES | REVIEW_STAGES + and self.stage != STAGE_REJECTED + ): return True return False @@ -1172,16 +1604,17 @@ def funders(self): return ArticleFunding.objects.filter(article=self) def __str__(self): - return u'%s - %s' % (self.pk, truncatesmart(self.title)) + return "%s - %s" % (self.pk, truncatesmart(self.title)) @staticmethod @cache(300) def get_article(journal, identifier_type, identifier): from identifiers import models as identifier_models + try: article = None # resolve an article from an identifier type and an identifier - if identifier_type.lower() == 'id': + if identifier_type.lower() == "id": # this is the hardcoded fallback type: using built-in id article = Article.objects.filter( id=identifier, @@ -1198,26 +1631,28 @@ def get_article(journal, identifier_type, identifier): return None return article - except BaseException: # no article found + except BaseException: # no article found # TODO: handle better and log return None @staticmethod def get_press_article(press, identifier_type, identifier): from identifiers import models as identifier_models + try: article = None # resolve an article from an identifier type and an identifier - if identifier_type.lower() == 'id': + if identifier_type.lower() == "id": # this is the hardcoded fallback type: using built-in id article = Article.objects.filter(id=identifier)[0] else: # this looks up an article by an ID type and an identifier string article = identifier_models.Identifier.objects.filter( - id_type=identifier_type, identifier=identifier)[0].article + id_type=identifier_type, identifier=identifier + )[0].article return article - except BaseException: # no article found + except BaseException: # no article found # TODO: handle better and log return None @@ -1229,22 +1664,23 @@ def url(self): @property def local_url(self): from identifiers import models as identifier_models + try: identifier = identifier_models.Identifier.objects.get( - id_type='pubid', + id_type="pubid", article=self, ) except identifier_models.Identifier.DoesNotExist: identifier = identifier_models.Identifier( - id_type="id", - identifier=self.pk, - article=self + id_type="id", identifier=self.pk, article=self ) url = reverse( - 'article_view', - kwargs={'identifier_type': identifier.id_type, - 'identifier': identifier.identifier} + "article_view", + kwargs={ + "identifier_type": identifier.id_type, + "identifier": identifier.identifier, + }, ) return url @@ -1252,83 +1688,96 @@ def local_url(self): @property def pdf_url(self): pdfs = self.pdfs - path = reverse('article_download_galley', kwargs={ - 'article_id': self.pk, - 'galley_id': pdfs[0].pk - }) + path = reverse( + "article_download_galley", + kwargs={"article_id": self.pk, "galley_id": pdfs[0].pk}, + ) return self.journal.site_url(path=path) def get_remote_url(self, request): - parsed_uri = urlparse('http' + ('', 's')[request.is_secure()] + '://' + request.META['HTTP_HOST']) - domain = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_uri) + parsed_uri = urlparse( + "http" + ("", "s")[request.is_secure()] + "://" + request.META["HTTP_HOST"] + ) + domain = "{uri.scheme}://{uri.netloc}".format(uri=parsed_uri) url = domain + self.local_url return url def step_to_url(self): funding_enabled = False - if self.journal and getattr( - self.journal, "submissionconfiguration", None - ): + if self.journal and getattr(self.journal, "submissionconfiguration", None): funding_enabled = self.journal.submissionconfiguration.funding if self.current_step == 1: - return reverse('submit_info', kwargs={'article_id': self.id}) + return reverse("submit_info", kwargs={"article_id": self.id}) elif self.current_step == 2: - return reverse('submit_authors', kwargs={'article_id': self.id}) + return reverse("submit_authors", kwargs={"article_id": self.id}) elif self.current_step == 3: - return reverse('submit_files', kwargs={'article_id': self.id}) + return reverse("submit_files", kwargs={"article_id": self.id}) elif self.current_step == 4 and funding_enabled: - return reverse('submit_funding', kwargs={'article_id': self.id}) + return reverse("submit_funding", kwargs={"article_id": self.id}) elif self.current_step == 4: - return reverse('submit_review', kwargs={'article_id': self.id}) + return reverse("submit_review", kwargs={"article_id": self.id}) else: - return reverse('submit_review', kwargs={'article_id': self.id}) + return reverse("submit_review", kwargs={"article_id": self.id}) def step_name(self): funding_enabled = False - if self.journal and getattr( - self.journal, "submissionconfiguration", None - ): + if self.journal and getattr(self.journal, "submissionconfiguration", None): funding_enabled = self.journal.submissionconfiguration.funding if self.current_step == 1: - return 'Article Information' + return "Article Information" elif self.current_step == 2: - return 'Article Authors' + return "Article Authors" elif self.current_step == 3: - return 'Article Files' + return "Article Files" elif self.current_step == 4 and funding_enabled: - return 'Article Funding' + return "Article Funding" elif self.current_step == 4: - return 'Review Article Submission' + return "Review Article Submission" elif self.current_step == 5 and funding_enabled: - return 'Review Article Submission' + return "Review Article Submission" else: - return 'Submission Complete' + return "Submission Complete" def save(self, *args, **kwargs): if self.pk is not None: current_object = Article.objects.get(pk=self.pk) if current_object.stage != self.stage: - ArticleStageLog.objects.create(article=self, stage_from=current_object.stage, - stage_to=self.stage) + ArticleStageLog.objects.create( + article=self, stage_from=current_object.stage, stage_to=self.stage + ) super(Article, self).save(*args, **kwargs) def folder_path(self): - return os.path.join(settings.BASE_DIR, 'files', 'articles', str(self.pk)) + return os.path.join(settings.BASE_DIR, "files", "articles", str(self.pk)) def production_managers(self): - return [assignment.production_manager for assignment in self.productionassignment_set.all()] + return [ + assignment.production_manager + for assignment in self.productionassignment_set.all() + ] def editor_list(self): return [assignment.editor for assignment in self.editorassignment_set.all()] def editors(self): - return [{'editor': assignment.editor, 'editor_type': assignment.editor_type, 'assignment': assignment} for - assignment in self.editorassignment_set.all()] + return [ + { + "editor": assignment.editor, + "editor_type": assignment.editor_type, + "assignment": assignment, + } + for assignment in self.editorassignment_set.all() + ] def section_editors(self, emails=False): - editors = [assignment.editor for assignment in self.editorassignment_set.filter(editor_type='section-editor')] + editors = [ + assignment.editor + for assignment in self.editorassignment_set.filter( + editor_type="section-editor" + ) + ] if emails: return set([editor.email for editor in editors]) @@ -1337,7 +1786,9 @@ def section_editors(self, emails=False): return editors def editor_emails(self): - return [assignment.editor.email for assignment in self.editorassignment_set.all()] + return [ + assignment.editor.email for assignment in self.editorassignment_set.all() + ] def contact_emails(self): emails = [author.email for author in self.authors.all()] @@ -1356,18 +1807,21 @@ def peer_reviewers(self, emails=False, completed=False): def issues_list(self): from journal import models as journal_models - return journal_models.Issue.objects.filter(journal=self.journal, articles__in=[self]) + + return journal_models.Issue.objects.filter( + journal=self.journal, articles__in=[self] + ) @cache(7200) def altmetrics(self): alm = self.altmetric_set.all() return { - 'twitter': alm.filter(source='twitter'), - 'wikipedia': alm.filter(source='wikipedia'), - 'reddit': alm.filter(source='reddit'), - 'hypothesis': alm.filter(source='hypothesis'), - 'wordpress': alm.filter(source='wordpress.com'), - 'crossref': alm.filter(source='crossref'), + "twitter": alm.filter(source="twitter"), + "wikipedia": alm.filter(source="wikipedia"), + "reddit": alm.filter(source="reddit"), + "hypothesis": alm.filter(source="hypothesis"), + "wordpress": alm.filter(source="wordpress.com"), + "crossref": alm.filter(source="crossref"), } @property @@ -1391,23 +1845,27 @@ def issue(self): @property def issue_title(self): - """ The issue title in the context of the article + """The issue title in the context of the article When an article renders its issue title, it can include article dependant elements such as page ranges or article numbers. For this reason, we cannot render database cached issue title. """ if not self.issue: - return '' + return "" - if self.issue.issue_type.code != 'issue': + if self.issue.issue_type.code != "issue": return self.issue.issue_title else: - template = Template(" • ".join([ - title_part - for title_part in self.issue.issue_title_parts(article=self) - if title_part - ])) + template = Template( + " • ".join( + [ + title_part + for title_part in self.issue.issue_title_parts(article=self) + if title_part + ] + ) + ) return mark_safe(template.render(Context())) def author_list(self): @@ -1424,7 +1882,7 @@ def bibtex_author_list(self): def keyword_list_str(self, separator=","): if self.keywords.exists(): return separator.join(kw.word for kw in self.keywords.all()) - return '' + return "" def can_edit(self, user): # returns True if a user can edit an article @@ -1452,23 +1910,27 @@ def can_edit(self, user): def current_review_round(self): try: - return self.reviewround_set.all().order_by('-round_number')[0].round_number + return self.reviewround_set.all().order_by("-round_number")[0].round_number except IndexError: return 1 def current_review_round_object(self): try: - return self.reviewround_set.all().order_by('-round_number')[0] + return self.reviewround_set.all().order_by("-round_number")[0] except IndexError: return None @property def active_reviews(self): - return self.reviewassignment_set.filter(is_complete=False, date_declined__isnull=True) + return self.reviewassignment_set.filter( + is_complete=False, date_declined__isnull=True + ) @property def completed_reviews(self): - return self.reviewassignment_set.filter(is_complete=True, date_declined__isnull=True) + return self.reviewassignment_set.filter( + is_complete=True, date_declined__isnull=True + ) @property def completed_reviews_with_permission(self): @@ -1484,7 +1946,7 @@ def completed_reviews_with_decision(self): is_complete=True, date_declined__isnull=True, decision__isnull=False, - ).exclude(decision='withdrawn') + ).exclude(decision="withdrawn") @property def completed_reviews_with_decision_previous_rounds(self): @@ -1496,21 +1958,18 @@ def completed_reviews_with_decision_previous_rounds(self): def active_review_request_for_user(self, user): try: return self.reviewassignment_set.filter( - is_complete=False, - date_declined__isnull=True, - reviewer=user + is_complete=False, date_declined__isnull=True, reviewer=user ).first() except review_models.ReviewAssignment.DoesNotExist: return None def reviews_not_withdrawn(self): - return self.reviewassignment_set.exclude(decision='withdrawn') + return self.reviewassignment_set.exclude(decision="withdrawn") def number_of_withdrawn_reviews(self): - return self.reviewassignment_set.filter(decision='withdrawn').count() + return self.reviewassignment_set.filter(decision="withdrawn").count() def accept_article(self, stage=None): - # Frozen author records should be updated at acceptance, # so we fire the default force_update=True on snapshot_authors self.snapshot_authors() @@ -1537,9 +1996,11 @@ def decline_article(self): self.date_accepted = None self.stage = STAGE_REJECTED - self.incomplete_reviews().update(decision=RD.DECISION_WITHDRAWN.value, - date_complete=timezone.now(), - is_complete=True,) + self.incomplete_reviews().update( + decision=RD.DECISION_WITHDRAWN.value, + date_complete=timezone.now(), + is_complete=True, + ) self.save() def undo_review_decision(self): @@ -1557,7 +2018,9 @@ def accept_preprint(self, date, time): self.date_accepted = timezone.now() self.date_declined = None self.stage = STAGE_PREPRINT_PUBLISHED - self.date_published = dateparser.parse('{date} {time}'.format(date=date, time=time)) + self.date_published = dateparser.parse( + "{date} {time}".format(date=date, time=time) + ) self.save() def mark_reviews_shared(self): @@ -1588,8 +2051,11 @@ def get_next_galley_sequence(self): @property def is_published(self): - if (self.stage == STAGE_PUBLISHED or self.stage == STAGE_PREPRINT_PUBLISHED) and \ - self.date_published and self.date_published < timezone.now(): + if ( + (self.stage == STAGE_PUBLISHED or self.stage == STAGE_PREPRINT_PUBLISHED) + and self.date_published + and self.date_published < timezone.now() + ): return True else: return False @@ -1599,13 +2065,15 @@ def scheduled_for_publication(self): return bool(self.stage == STAGE_PUBLISHED and self.date_published) def snapshot_authors(self, article=None, force_update=True): - """ Creates/updates FrozenAuthor records for this article's authors + """Creates/updates FrozenAuthor records for this article's authors :param article: (deprecated) should not pass this argument :param force_update: (bool) Whether or not to update existing records """ - subq = models.Subquery(ArticleAuthorOrder.objects.filter( - article=self, author__id=models.OuterRef("id") - ).values_list("order")) + subq = models.Subquery( + ArticleAuthorOrder.objects.filter( + article=self, author__id=models.OuterRef("id") + ).values_list("order") + ) authors = self.authors.annotate(order=subq).order_by("order") for author in authors: author.snapshot_self(self, force_update) @@ -1638,7 +2106,7 @@ def active_author_copyedits(self): @property def custom_fields(self): - """ Returns all the FieldAnswers configured for rendering""" + """Returns all the FieldAnswers configured for rendering""" return self.fieldanswer_set.filter( field__display=True, answer__isnull=False, @@ -1649,20 +2117,30 @@ def get_meta_image_path(self): if self.meta_image and self.meta_image.url: path = self.meta_image.url elif self.large_image_file and self.large_image_file.id: - path = reverse('article_file_download', kwargs={'identifier_type': 'id', - 'identifier': self.pk, - 'file_id': self.large_image_file.pk}) + path = reverse( + "article_file_download", + kwargs={ + "identifier_type": "id", + "identifier": self.pk, + "file_id": self.large_image_file.pk, + }, + ) elif self.thumbnail_image_file and self.thumbnail_image_file.id: - path = reverse('article_file_download', kwargs={'identifier_type': 'id', - 'identifier': self.pk, - 'file_id': self.thumbnail_image_file.pk}) + path = reverse( + "article_file_download", + kwargs={ + "identifier_type": "id", + "identifier": self.pk, + "file_id": self.thumbnail_image_file.pk, + }, + ) elif self.journal.default_large_image: path = self.journal.default_large_image.url if path: return self.journal.site_url(path=path) else: - return '' + return "" def unlink_meta_file(self): path = os.path.join(self.meta_image.storage.base_location, self.meta_image.name) @@ -1670,7 +2148,9 @@ def unlink_meta_file(self): os.unlink(path) def next_author_sort(self): - current_orders = [order.order for order in ArticleAuthorOrder.objects.filter(article=self)] + current_orders = [ + order.order for order in ArticleAuthorOrder.objects.filter(article=self) + ] if not current_orders: return 0 else: @@ -1678,7 +2158,7 @@ def next_author_sort(self): def subject_editors(self): editors = list() - subjects = self.subject_set.all().prefetch_related('editors') + subjects = self.subject_set.all().prefetch_related("editors") for subject in subjects: for editor in subject.editors.all(): @@ -1724,19 +2204,19 @@ def current_workflow_element(self): @property def current_workflow_element_url(self): - kwargs = {'article_id': self.pk} + kwargs = {"article_id": self.pk} # STAGE_UNASSIGNED and STAGE_PUBLISHED arent elements so are hardcoded. if self.stage == STAGE_UNASSIGNED: - path = reverse('review_unassigned_article', kwargs=kwargs) + path = reverse("review_unassigned_article", kwargs=kwargs) elif self.stage in FINAL_STAGES: - path = reverse('manage_archive_article', kwargs=kwargs) + path = reverse("manage_archive_article", kwargs=kwargs) elif not self.stage: logger.error( - 'Article #{} has no Stage.'.format( + "Article #{} has no Stage.".format( self.pk, ) ) - return '?workflow_element_url=no_stage' + return "?workflow_element_url=no_stage" else: element = self.current_workflow_element if element: @@ -1745,11 +2225,11 @@ def current_workflow_element_url(self): # In order to ensure the Dashboard renders we purposefully do # not raise an error message here. logger.error( - 'There is no workflow element for stage {}.'.format( + "There is no workflow element for stage {}.".format( self.stage, ) ) - return '?workflow_element_url=no_element' + return "?workflow_element_url=no_element" return self.journal.site_url(path=path) def next_workflow_element(self): @@ -1757,9 +2237,9 @@ def next_workflow_element(self): current_workflow_element = self.current_workflow_element journal_elements = list(self.journal.workflow().elements.all()) i = journal_elements.index(current_workflow_element) - return journal_elements[i+1] + return journal_elements[i + 1] except (IndexError, ValueError): - return 'No next workflow stage found' + return "No next workflow stage found" @cache(600) def render_sample_doi(self): @@ -1792,13 +2272,13 @@ def close_core_workflow_objects(self): copyedit_acknowledged=True, copyedit_accepted=timezone.now(), date_decided=timezone.now(), - decision='cancelled', + decision="cancelled", ) copyedit_models.AuthorReview.objects.filter( assignment__article=self, date_decided__isnull=True, ).update( - decision='accept', + decision="accept", date_decided=timezone.now(), ) @@ -1854,19 +2334,19 @@ def hidden_completed_reviews(self): date_complete__isnull=False, for_author_consumption=False, ).exclude( - decision='withdrawn', + decision="withdrawn", ) def incomplete_reviews(self): - return self.reviewassignment_set.filter(is_complete=False, - date_declined__isnull=True, - decision__isnull=True) + return self.reviewassignment_set.filter( + is_complete=False, date_declined__isnull=True, decision__isnull=True + ) def ms_and_figure_files(self): return chain(self.manuscript_files.all(), self.data_figure_files.all()) def fast_last_modified_date(self): - """ A faster way of calculating an approximate last modified date + """A faster way of calculating an approximate last modified date While not as accurate as `best_last_modified_date` this calculation covers most of the relevant relations when determining when an article has been last modified. Depending on the numner of related nodes, this @@ -1877,29 +2357,32 @@ def fast_last_modified_date(self): try: latest = self.galley_set.latest("last_modified").last_modified if latest > last_mod_date: - last_mod_date = latest + last_mod_date = latest except core_models.Galley.DoesNotExist: pass try: latest = self.frozenauthor_set.latest("last_modified").last_modified if latest > last_mod_date: - last_mod_date = latest + last_mod_date = latest except FrozenAuthor.DoesNotExist: pass try: - latest = core_models.File.objects.filter( - article_id=self.pk).latest("last_modified").last_modified + latest = ( + core_models.File.objects.filter(article_id=self.pk) + .latest("last_modified") + .last_modified + ) if latest > last_mod_date: - last_mod_date = latest + last_mod_date = latest except core_models.File.DoesNotExist: pass try: latest = self.issues.latest("last_modified").last_modified if latest > last_mod_date: - last_mod_date = latest + last_mod_date = latest except journal_models.Journal.DoesNotExist: pass @@ -1916,13 +2399,13 @@ def pinned(self): class FrozenAuthor(AbstractLastModifiedModel): article = models.ForeignKey( - 'submission.Article', + "submission.Article", blank=True, null=True, on_delete=models.CASCADE, ) author = models.ForeignKey( - 'core.Account', + "core.Account", blank=True, null=True, on_delete=models.SET_NULL, @@ -1946,21 +2429,21 @@ class FrozenAuthor(AbstractLastModifiedModel): validators=[plain_text_validator], ) middle_name = models.CharField( - max_length=300, - blank=True, - validators=[plain_text_validator], + max_length=300, + blank=True, + validators=[plain_text_validator], ) last_name = models.CharField( max_length=300, blank=True, validators=[plain_text_validator], -) + ) institution = models.CharField( max_length=1000, blank=True, validators=[plain_text_validator], -) + ) department = models.CharField( max_length=300, blank=True, @@ -1968,16 +2451,17 @@ class FrozenAuthor(AbstractLastModifiedModel): ) frozen_biography = JanewayBleachField( blank=True, - verbose_name=_('Frozen Biography'), - help_text=_("The author's biography at the time they published" - " the linked article. For this article only, it overrides" - " any main biography attached to the author's account." - " If Frozen Biography is left blank, any main biography" - " for the account will be populated instead." - ), + verbose_name=_("Frozen Biography"), + help_text=_( + "The author's biography at the time they published" + " the linked article. For this article only, it overrides" + " any main biography attached to the author's account." + " If Frozen Biography is left blank, any main biography" + " for the account will be populated instead." + ), ) country = models.ForeignKey( - 'core.Country', + "core.Country", null=True, blank=True, on_delete=models.SET_NULL, @@ -1986,30 +2470,33 @@ class FrozenAuthor(AbstractLastModifiedModel): order = models.PositiveIntegerField(default=1) is_corporate = models.BooleanField( - default=False, - help_text="If enabled, the institution and department fields will " - "be used as the author full name", + default=False, + help_text="If enabled, the institution and department fields will " + "be used as the author full name", ) frozen_email = models.EmailField( - blank=True, - verbose_name=_("Author Email"), + blank=True, + verbose_name=_("Author Email"), ) frozen_orcid = models.CharField( max_length=40, blank=True, - verbose_name=_('ORCiD'), - help_text=_("ORCiD to be displayed when no account is" - " associated with this author. It should be introduced in code " - "format (e.g: 0000-0000-0000-000X)" - ) + verbose_name=_("ORCiD"), + help_text=_( + "ORCiD to be displayed when no account is" + " associated with this author. It should be introduced in code " + "format (e.g: 0000-0000-0000-000X)" + ), ) display_email = models.BooleanField( default=False, - help_text=_("If checked, this authors email address link will be displayed on the article page.") + help_text=_( + "If checked, this authors email address link will be displayed on the article page." + ), ) class Meta: - ordering = ('order', 'pk') + ordering = ("order", "pk") def __str__(self): return self.full_name() @@ -2022,7 +2509,7 @@ def full_name(self): self.first_name, self.middle_name, self.last_name, - self.name_suffix + self.name_suffix, ] return " ".join([each for each in name_elements if each]) @@ -2036,8 +2523,8 @@ def dc_name_string(self): if self.last_name: name_string += "{}{}{} ".format( self.last_name, - " {}".format(self.name_suffix) if self.name_suffix else '', - "," if self.first_name else '', + " {}".format(self.name_suffix) if self.name_suffix else "", + "," if self.first_name else "", ) if self.first_name: name_string += "{}".format(self.first_name) @@ -2074,26 +2561,28 @@ def biography(self): return self.author.biography return None - def citation_name(self): if self.is_corporate: return self.corporate_name - first_initial, middle_initial = '', '' + first_initial, middle_initial = "", "" if self.middle_name: - middle_initial = ' {0}.'.format(self.middle_name[:1]) + middle_initial = " {0}.".format(self.middle_name[:1]) if self.first_name: - first_initial = '{0}.'.format(self.first_name[:1]) + first_initial = "{0}.".format(self.first_name[:1]) - citation = '{last}, {first}{middle}'.format( - last=self.last_name, first=first_initial, middle=middle_initial) + citation = "{last}, {first}{middle}".format( + last=self.last_name, first=first_initial, middle=middle_initial + ) if self.name_suffix: - citation = '{}, {}'.format(citation, self.name_suffix) + citation = "{}, {}".format(citation, self.name_suffix) return citation def given_names(self): if self.middle_name: - return '{first_name} {middle_name}'.format(first_name=self.first_name, middle_name=self.middle_name) + return "{first_name} {middle_name}".format( + first_name=self.first_name, middle_name=self.middle_name + ) else: return self.first_name @@ -2103,13 +2592,12 @@ def affiliation(self): elif self.institution: return self.institution else: - return '' + return "" @property def is_correspondence_author(self): # early return if no email address available - if (not self.author - or settings.DUMMY_EMAIL_DOMAIN in self.author.email): + if not self.author or settings.DUMMY_EMAIL_DOMAIN in self.author.email: return False elif self.article.journal.enable_correspondence_authors is True: @@ -2117,8 +2605,8 @@ def is_correspondence_author(self): return self.article.correspondence_author == self.author else: order = ArticleAuthorOrder.objects.get( - article=self.article, - author=self.author, + article=self.article, + author=self.author, ).order return order == 0 else: @@ -2127,26 +2615,26 @@ def is_correspondence_author(self): class Section(AbstractLastModifiedModel): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) number_of_reviewers = models.IntegerField(default=2) editors = models.ManyToManyField( - 'core.Account', + "core.Account", help_text="Editors assigned will be notified of submissions," - " overruling the notification settings for the journal.", + " overruling the notification settings for the journal.", ) section_editors = models.ManyToManyField( - 'core.Account', + "core.Account", help_text="Section editors assigned will be notified of submissions," - " overruling the notification settings for the journal.", - related_name='section_editors', + " overruling the notification settings for the journal.", + related_name="section_editors", ) auto_assign_editors = models.BooleanField( default=False, help_text="Articles submitted to this section will be automatically" - " assigned to the editors and/or section editors selected above.", + " assigned to the editors and/or section editors selected above.", ) is_filterable = models.BooleanField( default=True, @@ -2154,12 +2642,12 @@ class Section(AbstractLastModifiedModel): ) public_submissions = models.BooleanField(default=True) indexing = models.BooleanField( - default=True, - help_text="Whether this section is put forward for indexing") + default=True, help_text="Whether this section is put forward for indexing" + ) sequence = models.PositiveIntegerField( default=0, help_text="Determines the order in which the section is rendered" - " Sections can also be reorder by drag-and-drop", + " Sections can also be reorder by drag-and-drop", ) name = models.CharField( max_length=200, @@ -2169,14 +2657,13 @@ class Section(AbstractLastModifiedModel): max_length=200, null=True, blank=True, - help_text="Pluralised name for the section" - " (e.g: Article -> Articles)", + help_text="Pluralised name for the section" " (e.g: Article -> Articles)", ) objects = model_utils.JanewayMultilingualManager() class Meta: - ordering = ('sequence',) + ordering = ("sequence",) def __str__(self): if self.name: @@ -2209,7 +2696,9 @@ def section_editor_emails(self): return [editor.email for editor in self.section_editors.all()] def all_editor_emails(self): - return [editor.email for editor in self.section_editors.all() + self.editors.all()] + return [ + editor.email for editor in self.section_editors.all() + self.editors.all() + ] def issue_display(self): if self.plural: @@ -2218,8 +2707,12 @@ def issue_display(self): class Licence(AbstractLastModifiedModel): - journal = models.ForeignKey('journal.Journal', null=True, blank=True, on_delete=models.SET_NULL) - press = models.ForeignKey('press.Press', null=True, blank=True, on_delete=models.SET_NULL) + journal = models.ForeignKey( + "journal.Journal", null=True, blank=True, on_delete=models.SET_NULL + ) + press = models.ForeignKey( + "press.Press", null=True, blank=True, on_delete=models.SET_NULL + ) name = models.CharField(max_length=300) short_name = models.CharField(max_length=15) @@ -2230,10 +2723,10 @@ class Licence(AbstractLastModifiedModel): available_for_submission = models.BooleanField(default=True) class Meta: - ordering = ('order', 'name') + ordering = ("order", "name") def __str__(self): - return '{short_name}'.format(short_name=self.short_name) + return "{short_name}".format(short_name=self.short_name) def object(self): if not self.journal: @@ -2248,7 +2741,7 @@ class Note(models.Model): on_delete=models.CASCADE, ) creator = models.ForeignKey( - 'core.Account', + "core.Account", null=True, on_delete=models.SET_NULL, ) @@ -2256,56 +2749,59 @@ class Note(models.Model): date_time = models.DateTimeField(auto_now_add=True) class Meta: - ordering = ('-date_time',) + ordering = ("-date_time",) def field_kind_choices(): return ( - ('text', 'Text Field'), - ('textarea', 'Text Area'), - ('check', 'Check Box'), - ('select', 'Select'), - ('email', 'Email'), - ('date', 'Date'), + ("text", "Text Field"), + ("textarea", "Text Area"), + ("check", "Check Box"), + ("select", "Select"), + ("email", "Email"), + ("date", "Date"), ) def width_choices(): return ( - ('third', 'Third'), - ('half', 'Half'), - ('full,', 'Full'), + ("third", "Third"), + ("half", "Half"), + ("full,", "Full"), ) class Field(models.Model): journal = models.ForeignKey( - 'journal.Journal', + "journal.Journal", blank=True, null=True, on_delete=models.SET_NULL, ) press = models.ForeignKey( - 'press.Press', + "press.Press", blank=True, null=True, on_delete=models.SET_NULL, ) name = models.CharField(max_length=200) kind = models.CharField(max_length=50, choices=field_kind_choices()) - width = models.CharField(max_length=50, choices=width_choices(), default='full') - choices = models.CharField(max_length=1000, null=True, blank=True, - help_text='Separate choices with the bar | character.') + width = models.CharField(max_length=50, choices=width_choices(), default="full") + choices = models.CharField( + max_length=1000, + null=True, + blank=True, + help_text="Separate choices with the bar | character.", + ) required = models.BooleanField(default=True) order = models.IntegerField() display = models.BooleanField( - default=False, - help_text='Whether or not display this field in the article page' + default=False, help_text="Whether or not display this field in the article page" ) help_text = models.TextField() class Meta: - ordering = ('order', 'name') + ordering = ("order", "name") def __str__(self): return "Field: {0} ({1})".format(self.name, self.kind) @@ -2333,18 +2829,18 @@ class ArticleAuthorOrder(models.Model): on_delete=models.CASCADE, ) author = models.ForeignKey( - 'core.Account', + "core.Account", on_delete=models.CASCADE, ) order = models.PositiveIntegerField(default=0) class Meta: - ordering = ('order',) + ordering = ("order",) class SubmissionConfiguration(models.Model): journal = models.OneToOneField( - 'journal.Journal', + "journal.Journal", on_delete=models.CASCADE, ) @@ -2364,14 +2860,14 @@ class SubmissionConfiguration(models.Model): figures_data = models.BooleanField( default=True, - verbose_name=_('Figures and Data Files'), + verbose_name=_("Figures and Data Files"), ) default_license = models.ForeignKey( Licence, null=True, blank=True, - help_text=_('The default license applied when no option is presented'), + help_text=_("The default license applied when no option is presented"), on_delete=models.SET_NULL, ) default_language = models.CharField( @@ -2379,36 +2875,35 @@ class SubmissionConfiguration(models.Model): null=True, blank=True, choices=LANGUAGE_CHOICES, - help_text=_('The default language of articles when lang is hidden'), + help_text=_("The default language of articles when lang is hidden"), ) default_section = models.ForeignKey( Section, null=True, blank=True, - help_text=_('The default section of ' - 'articles when no option is presented'), + help_text=_("The default section of " "articles when no option is presented"), on_delete=models.SET_NULL, ) submission_file_text = models.CharField( max_length=255, - default='Manuscript File', - help_text='During submission the author will be asked to upload a file' - 'that is considered the main text of the article. You can use' - 'this field to change the label for that file in submission.', + default="Manuscript File", + help_text="During submission the author will be asked to upload a file" + "that is considered the main text of the article. You can use" + "this field to change the label for that file in submission.", ) def __str__(self): - return 'SubmissionConfiguration for {0}'.format(self.journal.name) + return "SubmissionConfiguration for {0}".format(self.journal.name) def lang_section_license_width(self): if self.language and self.license: - return '4' + return "4" elif not self.language and not self.license: - return '12' + return "12" elif not self.language and self.license: - return '6' + return "6" elif self.language and not self.license: - return '6' + return "6" def handle_defaults(self, article): if not self.section and self.default_section: @@ -2425,6 +2920,7 @@ def handle_defaults(self, article): # Signals + @receiver(pre_delete, sender=FrozenAuthor) def remove_author_from_article(sender, instance, **kwargs): """ @@ -2453,16 +2949,18 @@ def remove_author_from_article(sender, instance, **kwargs): def order_keywords(sender, instance, action, reverse, model, pk_set, **kwargs): - if action == 'post_add': + if action == "post_add": try: - latest = KeywordArticle.objects.filter( - article=instance).latest("order").order + latest = ( + KeywordArticle.objects.filter(article=instance).latest("order").order + ) except KeywordArticle.DoesNotExist: latest = 0 for pk in pk_set: latest += 1 keyword_article = KeywordArticle.objects.get( - keyword__pk=pk, article=instance) + keyword__pk=pk, article=instance + ) if keyword_article.order == 1 != latest: keyword_article.order = latest keyword_article.save() diff --git a/src/submission/templatetags/stages.py b/src/submission/templatetags/stages.py index 99653c8354..a04abbacee 100644 --- a/src/submission/templatetags/stages.py +++ b/src/submission/templatetags/stages.py @@ -14,11 +14,11 @@ def in_stage_group(attribute, stage_group): :return: boolean """ - if stage_group == 'review': + if stage_group == "review": stages = models.REVIEW_STAGES - elif stage_group == 'copyediting': + elif stage_group == "copyediting": stages = models.COPYEDITING_STAGES - elif stage_group == 'preprint': + elif stage_group == "preprint": stages = models.PREPRINT_STAGES else: stages = [] @@ -27,5 +27,3 @@ def in_stage_group(attribute, stage_group): return True return False - - diff --git a/src/submission/tests.py b/src/submission/tests.py index de565fe4ba..0cd2d7640a 100644 --- a/src/submission/tests.py +++ b/src/submission/tests.py @@ -39,17 +39,12 @@ class SubmissionTests(TestCase): - roles_path = os.path.join( - settings.BASE_DIR, - 'utils', - 'install', - 'roles.json' - ) + roles_path = os.path.join(settings.BASE_DIR, "utils", "install", "roles.json") fixtures = [roles_path] def test_new_journals_has_submission_configuration(self): if not self.journal_one.submissionconfiguration: - self.fail('Journal does not have a submissionconfiguration object.') + self.fail("Journal does not have a submissionconfiguration object.") @staticmethod def create_journal(): @@ -69,22 +64,22 @@ def create_journal(): @staticmethod def create_authors(): author_1_data = { - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "is_active": True, + "password": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } author_2_data = { - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Sr.', - 'first_name': 'Mauro', - 'last_name': 'Sanchez', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "is_active": True, + "password": "this_is_a_password", + "salutation": "Sr.", + "first_name": "Mauro", + "last_name": "Sanchez", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } author_1 = Account.objects.create(email="1@t.t", **author_1_data) author_2 = Account.objects.create(email="2@t.t", **author_2_data) @@ -93,13 +88,13 @@ def create_authors(): def create_sections(self): self.section_1 = models.Section.objects.create( - name='Test Public Section', + name="Test Public Section", journal=self.journal_one, ) self.section_2 = models.Section.objects.create( - name='Test Private Section', + name="Test Private Section", public_submissions=False, - journal=self.journal_one + journal=self.journal_one, ) self.section_3 = models.Section.objects.create( journal=self.journal_one, @@ -136,16 +131,16 @@ def test_article_image_galley(self): def test_article_how_to_cite(self): issue = journal_models.Issue.objects.create( - journal=self.journal_one, - issue="0", - volume=1, + journal=self.journal_one, + issue="0", + volume=1, ) article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", primary_issue=issue, date_published=FROZEN_DATETIME_2020, - page_numbers = "2-4" + page_numbers="2-4", ) author = models.FrozenAuthor.objects.create( article=article, @@ -168,12 +163,12 @@ def test_custom_article_how_to_cite(self): issue = journal_models.Issue.objects.create(journal=self.journal_one) journal_models.Issue article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", primary_issue=issue, date_published=FROZEN_DATETIME_2020, - page_numbers = "2-4", - custom_how_to_cite = "Banana", + page_numbers="2-4", + custom_how_to_cite="Banana", ) author = models.FrozenAuthor.objects.create( article=article, @@ -194,8 +189,7 @@ def test_funding_is_enabled_decorator_enabled(self): func = Mock() decorated = decorators.funding_is_enabled(func) decorated(request) - self.assertTrue(func.called, - "Funding pages not available when they should be") + self.assertTrue(func.called, "Funding pages not available when they should be") def test_funding_is_enabled_decorator_disabled(self): request = Mock() @@ -211,7 +205,7 @@ def test_funding_is_enabled_decorator_disabled(self): def test_snapshot_author_metadata_correctly(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", ) author, _ = self.create_authors() @@ -219,7 +213,7 @@ def test_snapshot_author_metadata_correctly(self): article.snapshot_authors() frozen = article.frozen_authors().all()[0] - keys = {'first_name', 'last_name', 'department', 'institution'} + keys = {"first_name", "last_name", "department", "institution"} self.assertDictEqual( {k: getattr(author, k) for k in keys}, @@ -228,7 +222,7 @@ def test_snapshot_author_metadata_correctly(self): def test_snapshot_author_order_correctly(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", ) author_1, author_2 = self.create_authors() @@ -245,7 +239,7 @@ def test_snapshot_author_order_correctly(self): def test_snapshot_author_metadata_do_not_override(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", ) author, _ = self.create_authors() @@ -259,13 +253,14 @@ def test_snapshot_author_metadata_do_not_override(self): article.snapshot_authors(force_update=False) self.assertEqual( - frozen.department, new_department, + frozen.department, + new_department, msg="Frozen author edits have been overriden by snapshot_authors", ) def test_snapshot_author_metadata_override(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", ) author, _ = self.create_authors() @@ -278,7 +273,8 @@ def test_snapshot_author_metadata_override(self): frozen = article.frozen_authors().all()[0] self.assertEqual( - frozen.department, author.department, + frozen.department, + author.department, msg="Frozen author edits have been overriden by snapshot_authors", ) @@ -314,7 +310,7 @@ def test_frozen_author_suffix(self): def test_snapshot_author_order_author_added_later(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test article", ) author_1, author_2 = self.create_authors() @@ -337,16 +333,13 @@ def test_snapshot_author_order_author_added_later(self): def test_article_keyword_default_order(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test of keywords", ) keywords = ["one", "two", "three", "four"] for i, kw in enumerate(keywords): kw_obj = models.Keyword.objects.create(word=kw) - models.KeywordArticle.objects.get_or_create( - keyword=kw_obj, - article=article - ) + models.KeywordArticle.objects.get_or_create(keyword=kw_obj, article=article) self.assertEqual( keywords, @@ -355,7 +348,7 @@ def test_article_keyword_default_order(self): def test_article_keyword_add(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test of keywords", ) keywords = ["one", "two", "three", "four"] @@ -370,7 +363,7 @@ def test_article_keyword_add(self): def test_article_keyword_remove(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test of keywords", ) keywords = ["one", "two", "three", "four"] @@ -390,7 +383,7 @@ def test_article_keyword_remove(self): def test_article_keyword_clear(self): article = models.Article.objects.create( - journal = self.journal_one, + journal=self.journal_one, title="Test article: a test of keywords", ) keywords = ["one", "two", "three", "four"] @@ -405,7 +398,7 @@ def test_article_keyword_clear(self): ) def test_edit_section(self): - """ Ensures editors can select sections that are not submissible""" + """Ensures editors can select sections that are not submissible""" article = models.Article.objects.create( journal=self.journal_one, title="Test article: a test of sections", @@ -433,7 +426,7 @@ def test_select_disabled_section_submit(self): form = forms.ArticleInfoSubmit(instance=article) self.assertTrue(section not in form.fields["section"].queryset) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_submit_info_view_form_selection_editor(self): article = models.Article.objects.create( journal=self.journal_one, @@ -450,13 +443,13 @@ def test_submit_info_view_form_selection_editor(self): ) clear_script_prefix() response = self.client.get( - reverse('submit_info', kwargs={'article_id': article.pk}), + reverse("submit_info", kwargs={"article_id": article.pk}), SERVER_NAME="testserver", ) self.assertEqual(response.status_code, 200) self.assertContains(response, section.__str__()) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_submit_info_view_form_selection_author(self): author_1, author_2 = self.create_authors() clear_cache() @@ -476,7 +469,7 @@ def test_submit_info_view_form_selection_author(self): ) clear_script_prefix() response = self.client.get( - reverse('submit_info', kwargs={'article_id': article.pk}), + reverse("submit_info", kwargs={"article_id": article.pk}), SERVER_NAME="testserver", ) self.assertEqual(response.status_code, 200) @@ -484,14 +477,16 @@ def test_submit_info_view_form_selection_author(self): def test_article_issue_title(self): from utils.testing import helpers + issue = helpers.create_issue( self.journal_one, vol=5, number=4, ) - issue.issue_title = 'Fall 2025' + issue.issue_title = "Fall 2025" from utils.logic import get_aware_datetime - issue.date = get_aware_datetime('2025-10-01') + + issue.date = get_aware_datetime("2025-10-01") issue.save() article = models.Article.objects.create( @@ -502,14 +497,12 @@ def test_article_issue_title(self): primary_issue=issue, ) - expected_article_issue_title = 'Volume 5 • Issue 4 • ' \ - '2025 • Fall 2025 • 3–5' + expected_article_issue_title = "Volume 5 • Issue 4 • " "2025 • Fall 2025 • 3–5" self.assertEqual(expected_article_issue_title, article.issue_title) - article.page_numbers='x–ix' + article.page_numbers = "x–ix" article.save() - expected_article_issue_title = 'Volume 5 • Issue 4 • ' \ - '2025 • Fall 2025 • x–ix' + expected_article_issue_title = "Volume 5 • Issue 4 • " "2025 • Fall 2025 • x–ix" self.assertEqual(expected_article_issue_title, article.issue_title) article.first_page = None @@ -517,32 +510,31 @@ def test_article_issue_title(self): article.page_numbers = None article.total_pages = 1 article.save() - expected_article_issue_title = 'Volume 5 • Issue 4 • ' \ - '2025 • Fall 2025 • 1 page' + expected_article_issue_title = ( + "Volume 5 • Issue 4 • " "2025 • Fall 2025 • 1 page" + ) self.assertEqual(expected_article_issue_title, article.issue_title) def test_url_based_orcid_cleaned(self): - clean_orcid = forms.utility_clean_orcid( - 'https://orcid.org/0000-0003-2126-266X' - ) + clean_orcid = forms.utility_clean_orcid("https://orcid.org/0000-0003-2126-266X") self.assertEqual( clean_orcid, - '0000-0003-2126-266X', + "0000-0003-2126-266X", ) def test_orcid_value_error_raised(self): with self.assertRaises(ValueError): - forms.utility_clean_orcid('Mauro-sfak-orci-dtst') + forms.utility_clean_orcid("Mauro-sfak-orci-dtst") def test_author_form_with_bad_orcid(self): form = forms.AuthorForm( { - 'first_name': 'Mauro', - 'last_name': 'Sanchez', - 'biography': 'Mauro is a Jedi Master hailing from the planet Galicia.', - 'institution': 'Birkbeck, University of London', - 'email': 'mauro@janeway.systems', - 'orcid': 'Mauro-sfak-orci-dtst', + "first_name": "Mauro", + "last_name": "Sanchez", + "biography": "Mauro is a Jedi Master hailing from the planet Galicia.", + "institution": "Birkbeck, University of London", + "email": "mauro@janeway.systems", + "orcid": "Mauro-sfak-orci-dtst", } ) self.assertFalse( @@ -552,50 +544,51 @@ def test_author_form_with_bad_orcid(self): def test_author_form_with_good_orcid(self): form = forms.AuthorForm( { - 'first_name': 'Andy', - 'last_name': 'Byers', - 'biography': 'Andy is a Jedi Master hailing from the planet Scotland.', - 'institution': 'Birkbeck, University of London', - 'email': 'andy@janeway.systems', - 'orcid': 'https://orcid.org/0000-0003-2126-266X', + "first_name": "Andy", + "last_name": "Byers", + "biography": "Andy is a Jedi Master hailing from the planet Scotland.", + "institution": "Birkbeck, University of London", + "email": "andy@janeway.systems", + "orcid": "https://orcid.org/0000-0003-2126-266X", } ) self.assertTrue( form.is_valid(), ) self.assertEqual( - form.cleaned_data.get('orcid'), - '0000-0003-2126-266X', + form.cleaned_data.get("orcid"), + "0000-0003-2126-266X", ) def test_author_form_harmful_inputs(self): harmful_string = ' This are not the droids you are looking for ' - for i, attr in enumerate({ - "first_name", - "last_name", - "middle_name", - "name_prefix", - "suffix", - "institution", - "department", - }): + for i, attr in enumerate( + { + "first_name", + "last_name", + "middle_name", + "name_prefix", + "suffix", + "institution", + "department", + } + ): form = forms.AuthorForm( { - 'first_name': 'Andy', - 'last_name': 'Byers', - 'biography': 'Andy', - 'institution': 'Birkbeck, University of London', - 'email': f'andy{i}@janeway.systems', - 'orcid': 'https://orcid.org/0000-0003-2126-266X', + "first_name": "Andy", + "last_name": "Byers", + "biography": "Andy", + "institution": "Birkbeck, University of London", + "email": f"andy{i}@janeway.systems", + "orcid": "https://orcid.org/0000-0003-2126-266X", **{attr: harmful_string}, } ) self.assertFalse( - form.is_valid(), - f"Harmful code injected into field '{attr}'" + form.is_valid(), f"Harmful code injected into field '{attr}'" ) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_article_encoding_bibtex(self): article = helpers.create_article( journal=self.journal_one, @@ -629,15 +622,13 @@ def test_article_encoding_bibtex(self): journal = {%s} } """ % (article.pk, article.pk, article.journal.issn, article.journal.name) - bibtex_lines = [ - line.strip() for line in bibtex.splitlines() if line.strip() - ] + bibtex_lines = [line.strip() for line in bibtex.splitlines() if line.strip()] expected_lines = [ line.strip() for line in expected.splitlines() if line.strip() ] self.assertEqual(bibtex_lines, expected_lines) - @override_settings(URL_CONFIG='domain') + @override_settings(URL_CONFIG="domain") def test_article_encoding_ris(self): article = helpers.create_article( journal=self.journal_one, @@ -669,9 +660,7 @@ def test_article_encoding_ris(self): UR - http://testserver/article/id/{article_id}/ ER - """.format(article_id=article.pk, journal_name=article.journal.name) - ris_lines = [ - line.strip() for line in ris.splitlines() if line.strip() - ] + ris_lines = [line.strip() for line in ris.splitlines() if line.strip()] expected_lines = [ line.strip() for line in expected.splitlines() if line.strip() ] @@ -680,35 +669,35 @@ def test_article_encoding_ris(self): def test_page_range_first_last(self): article = models.Article.objects.create( journal=self.journal_one, - title='Test article: A test of page ranges', + title="Test article: A test of page ranges", first_page=3, last_page=5, ) - self.assertEqual(article.page_range, '3–5') + self.assertEqual(article.page_range, "3–5") def test_page_range_first_only(self): article = models.Article.objects.create( journal=self.journal_one, - title='Test article: A test of page ranges', + title="Test article: A test of page ranges", first_page=3, ) - self.assertEqual(article.page_range, '3') + self.assertEqual(article.page_range, "3") def test_page_range_custom(self): article = models.Article.objects.create( journal=self.journal_one, - title='Test article: A test of page ranges', + title="Test article: A test of page ranges", first_page=3, last_page=5, - page_numbers='custom' + page_numbers="custom", ) - self.assertEqual(article.page_range, 'custom') + self.assertEqual(article.page_range, "custom") def test_editor_sees_non_public_sections(self): clear_cache() article = models.Article.objects.create( journal=self.journal_one, - title='Test article: Testing non public sections', + title="Test article: Testing non public sections", current_step=2, owner=self.editor, ) @@ -718,14 +707,12 @@ def test_editor_sees_non_public_sections(self): clear_script_prefix() response = self.client.get( reverse( - 'submit_info', - kwargs={'article_id': article.pk}, + "submit_info", + kwargs={"article_id": article.pk}, ), SERVER_NAME="testserver", ) - self.assertEqual( - response.status_code, 200 - ) + self.assertEqual(response.status_code, 200) self.assertContains( response, self.section_2.display_name_public_submission(), @@ -735,9 +722,9 @@ def test_author_doesnt_see_non_public_section(self): author = helpers.create_author(self.journal_one) article = models.Article.objects.create( journal=self.journal_one, - title='Test article: Testing public sections', + title="Test article: Testing public sections", current_step=2, - owner=author + owner=author, ) self.client.force_login( author, @@ -745,14 +732,12 @@ def test_author_doesnt_see_non_public_section(self): clear_script_prefix() response = self.client.get( reverse( - 'submit_info', - kwargs={'article_id': article.pk}, + "submit_info", + kwargs={"article_id": article.pk}, ), SERVER_NAME="testserver", ) - self.assertEqual( - response.status_code, 200 - ) + self.assertEqual(response.status_code, 200) self.assertNotContains( response, self.section_2.display_name_public_submission(), @@ -760,12 +745,7 @@ def test_author_doesnt_see_non_public_section(self): class ArticleSearchTests(TransactionTestCase): - roles_path = os.path.join( - settings.BASE_DIR, - 'utils', - 'install', - 'roles.json' - ) + roles_path = os.path.join(settings.BASE_DIR, "utils", "install", "roles.json") fixtures = [roles_path] @staticmethod @@ -786,22 +766,22 @@ def create_journal(): @staticmethod def create_authors(): author_1_data = { - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Prof.', - 'first_name': 'Martin', - 'last_name': 'Eve', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "is_active": True, + "password": "this_is_a_password", + "salutation": "Prof.", + "first_name": "Martin", + "last_name": "Eve", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } author_2_data = { - 'is_active': True, - 'password': 'this_is_a_password', - 'salutation': 'Sr.', - 'first_name': 'Mauro', - 'last_name': 'Sanchez', - 'department': 'English & Humanities', - 'institution': 'Birkbeck, University of London', + "is_active": True, + "password": "this_is_a_password", + "salutation": "Sr.", + "first_name": "Mauro", + "last_name": "Sanchez", + "department": "English & Humanities", + "institution": "Birkbeck, University of London", } author_1 = Account.objects.create(email="1@t.t", **author_1_data) author_2 = Account.objects.create(email="2@t.t", **author_1_data) @@ -826,6 +806,7 @@ def test_article_full_text_search(self): Computer, run a level-two diagnostic on warp-drive systems. """ from django.db import connection + if connection.vendor == "sqlite": # No native support for full text search in sqlite return @@ -896,7 +877,7 @@ def test_article_search_abstract(self): @override_settings(ENABLE_FULL_TEXT_SEARCH=True) def test_article_search_title(self): - text_to_search ="Computer, run a level-two diagnostic on warp-drive systems." + text_to_search = "Computer, run a level-two diagnostic on warp-drive systems." needle = "diagnostic" article = models.Article.objects.create( @@ -925,23 +906,22 @@ class FrozenAuthorModelTest(TestCase): @classmethod def setUpTestData(cls): cls.frozen_author = models.FrozenAuthor.objects.create( - name_prefix='Dr.', - first_name='S.', - middle_name='Bella', - last_name='Rogers', - name_suffix='Esq.', + name_prefix="Dr.", + first_name="S.", + middle_name="Bella", + last_name="Rogers", + name_suffix="Esq.", ) def test_full_name(self): - self.assertEqual('Dr. S. Bella Rogers Esq.', self.frozen_author.full_name()) + self.assertEqual("Dr. S. Bella Rogers Esq.", self.frozen_author.full_name()) class ArticleFormTests(TestCase): - def test_competing_interests_in_edit_article_metadata(self): form = forms.EditArticleMetadata() self.assertIn( - 'competing_interests', + "competing_interests", form.fields, "'competing_interests' should be present in EditArticleMetadata", ) @@ -949,7 +929,7 @@ def test_competing_interests_in_edit_article_metadata(self): def test_competing_interests_not_in_article_info_submit(self): form = forms.ArticleInfoSubmit() self.assertNotIn( - 'competing_interests', + "competing_interests", form.fields, "'competing_interests' should NOT be present in ArticleInfoSubmit", ) @@ -957,7 +937,7 @@ def test_competing_interests_not_in_article_info_submit(self): def test_competing_interests_not_in_editor_article_info_submit(self): form = forms.EditorArticleInfoSubmit() self.assertNotIn( - 'competing_interests', + "competing_interests", form.fields, - "'competing_interests' should NOT be present in EditorArticleInfoSubmit" + "'competing_interests' should NOT be present in EditorArticleInfoSubmit", ) diff --git a/src/submission/translation.py b/src/submission/translation.py index 30153ee232..cde10987f5 100644 --- a/src/submission/translation.py +++ b/src/submission/translation.py @@ -7,13 +7,14 @@ @register(models.Section) class SectionTranslationOptions(TranslationOptions): - fields = ('name', 'plural') + fields = ("name", "plural") @register(models.Article) class ArticleTranslationOptions(TranslationOptions): - fields = ('title', 'abstract') + fields = ("title", "abstract") + @register(models.SubmissionConfiguration) class ArticleTranslationOptions(TranslationOptions): - fields = ('submission_file_text',) + fields = ("submission_file_text",) diff --git a/src/submission/urls.py b/src/submission/urls.py index bf5f8db5ab..458d6fc8b8 100755 --- a/src/submission/urls.py +++ b/src/submission/urls.py @@ -7,38 +7,58 @@ from submission import views urlpatterns = [ - - re_path(r'^start/$', views.start, name='submission_start'), - re_path(r'^(?P[-\w.]+)/start/$', views.start, name='submission_start'), - re_path(r'^(?P\d+)/info/$', views.submit_info, name='submit_info'), - re_path(r'^(?P\d+)/authors/$', views.submit_authors, name='submit_authors'), - re_path(r'^(?P\d+)/authors/(?P\d+)/delete/$', views.delete_author, name='delete_author'), - re_path(r'^(?P\d+)/files/$', views.submit_files, name='submit_files'), - re_path(r'^(?P\d+)/funding/$', views.submit_funding, name='submit_funding'), - re_path(r'^submissions/$', views.submit_submissions, name='submission_submissions'), - re_path(r'^(?P\d+)/review/$', views.submit_review, name='submit_review'), - re_path( - r'^(?P\d+)/funders/(?P\d+)/delete/$', + re_path(r"^start/$", views.start, name="submission_start"), + re_path(r"^(?P[-\w.]+)/start/$", views.start, name="submission_start"), + re_path(r"^(?P\d+)/info/$", views.submit_info, name="submit_info"), + re_path( + r"^(?P\d+)/authors/$", views.submit_authors, name="submit_authors" + ), + re_path( + r"^(?P\d+)/authors/(?P\d+)/delete/$", + views.delete_author, + name="delete_author", + ), + re_path(r"^(?P\d+)/files/$", views.submit_files, name="submit_files"), + re_path( + r"^(?P\d+)/funding/$", views.submit_funding, name="submit_funding" + ), + re_path(r"^submissions/$", views.submit_submissions, name="submission_submissions"), + re_path( + r"^(?P\d+)/review/$", views.submit_review, name="submit_review" + ), + re_path( + r"^(?P\d+)/funders/(?P\d+)/delete/$", views.delete_funder, - name='delete_funder', + name="delete_funder", ), re_path( - r'^(?P\d+)/funders/(?P\d+)/edit/$', + r"^(?P\d+)/funders/(?P\d+)/edit/$", views.edit_funder, - name='edit_funder', + name="edit_funder", ), - - re_path(r'^manager/article/settings/article/(?P\d+)/publishernotes/order/$', views.publisher_notes_order, - name='submission_article_publisher_notes_order'), - - re_path(r'^manager/configurator/$', views.configurator, name='submission_configurator'), - - re_path(r'^manager/additional_fields/$', views.fields, name='submission_fields'), - re_path(r'^manager/additional_fields/(?P\d+)/$', views.fields, name='submission_fields_id'), - - re_path(r'^manager/licences/$', views.licenses, name='submission_licenses'), - re_path(r'^manager/licences/(?P\d+)/delete/', + re_path( + r"^manager/article/settings/article/(?P\d+)/publishernotes/order/$", + views.publisher_notes_order, + name="submission_article_publisher_notes_order", + ), + re_path( + r"^manager/configurator/$", views.configurator, name="submission_configurator" + ), + re_path(r"^manager/additional_fields/$", views.fields, name="submission_fields"), + re_path( + r"^manager/additional_fields/(?P\d+)/$", + views.fields, + name="submission_fields_id", + ), + re_path(r"^manager/licences/$", views.licenses, name="submission_licenses"), + re_path( + r"^manager/licences/(?P\d+)/delete/", views.delete_license, - name='submission_delete_license'), - re_path(r'^manager/licences/(?P\d+)/', views.licenses, name='submission_licenses_id'), + name="submission_delete_license", + ), + re_path( + r"^manager/licences/(?P\d+)/", + views.licenses, + name="submission_licenses_id", + ), ] diff --git a/src/submission/views.py b/src/submission/views.py index dc0afb4f74..71e86273f8 100755 --- a/src/submission/views.py +++ b/src/submission/views.py @@ -47,7 +47,7 @@ def start(request, type=None): form = forms.ArticleStart(journal=request.journal) if not request.user.is_author(request): - request.user.add_account_role('author', request.journal) + request.user.add_account_role("author", request.journal) if request.POST: form = forms.ArticleStart(request.POST, journal=request.journal) @@ -60,33 +60,30 @@ def start(request, type=None): new_article.article_agreement = logic.get_agreement_text(request.journal) new_article.save() - if type == 'preprint': + if type == "preprint": preprint_models.Preprint.objects.create(article=new_article) if setting_handler.get_setting( - 'general', - 'user_automatically_author', - request.journal, + "general", + "user_automatically_author", + request.journal, ).processed_value: logic.add_user_as_author(request.user, new_article) event_logic.Events.raise_event( event_logic.Events.ON_ARTICLE_SUBMISSION_START, - **{'request': request, 'article': new_article} + **{"request": request, "article": new_article}, ) return redirect( reverse( - 'submit_info', - kwargs={ - 'article_id': new_article.pk}, + "submit_info", + kwargs={"article_id": new_article.pk}, ), ) - template = 'admin/submission/start.html' - context = { - 'form': form - } + template = "admin/submission/start.html" + context = {"form": form} return render(request, template, context) @@ -103,9 +100,9 @@ def submit_submissions(request): stage=models.STAGE_UNSUBMITTED, ) - template = 'admin/submission/submission_submissions.html' + template = "admin/submission/submission_submissions.html" context = { - 'articles': articles, + "articles": articles, } return render(request, template, context) @@ -135,10 +132,10 @@ def submit_funding(request, article_id): ) if request.POST: - if 'next_step' in request.POST: + if "next_step" in request.POST: article.current_step = 5 article.save() - return redirect(reverse('submit_review', kwargs={'article_id': article_id})) + return redirect(reverse("submit_review", kwargs={"article_id": article_id})) funder_form = forms.ArticleFundingForm( request.POST, @@ -149,18 +146,18 @@ def submit_funding(request, article_id): funder_form.save() return redirect( reverse( - 'submit_funding', + "submit_funding", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) - template = 'admin/submission/submit_funding.html' + template = "admin/submission/submit_funding.html" context = { - 'article': article, - 'additional_fields': additional_fields, - 'funder_form': funder_form, + "article": article, + "additional_fields": additional_fields, + "funder_form": funder_form, } return render(request, template, context) @@ -186,8 +183,8 @@ def submit_info(request, article_id): ) additional_fields = models.Field.objects.filter(journal=request.journal) submission_summary = setting_handler.get_setting( - 'general', - 'submission_summary', + "general", + "submission_summary", request.journal, ).processed_value @@ -218,16 +215,16 @@ def submit_info(request, article_id): return redirect( reverse( - 'submit_authors', - kwargs={'article_id': article_id}, + "submit_authors", + kwargs={"article_id": article_id}, ), ) - template = 'admin/submission//submit_info.html' + template = "admin/submission//submit_info.html" context = { - 'article': article, - 'form': form, - 'additional_fields': additional_fields, + "article": article, + "form": form, + "additional_fields": additional_fields, } return render(request, template, context) @@ -236,7 +233,7 @@ def submit_info(request, article_id): @staff_member_required def publisher_notes_order(request, article_id): if request.POST: - ids = request.POST.getlist('note[]') + ids = request.POST.getlist("note[]") ids = [int(_id) for _id in ids] article = models.Article.objects.get( @@ -248,7 +245,7 @@ def publisher_notes_order(request, article_id): he.sequence = ids.index(he.pk) he.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @login_required @@ -270,30 +267,30 @@ def submit_authors(request, article_id): ) if article.current_step < 2 and not request.user.is_staff: - return redirect(reverse('submit_info', kwargs={'article_id': article_id})) + return redirect(reverse("submit_info", kwargs={"article_id": article_id})) form = forms.AuthorForm() error, modal = None, None - if request.GET.get('add_self', None) == 'True': + if request.GET.get("add_self", None) == "True": new_author = logic.add_user_as_author(request.user, article) messages.add_message( request, messages.SUCCESS, - _('{author_name} added to the article.').format( + _("{author_name} added to the article.").format( author_name=new_author.full_name(), ), ) return redirect( reverse( - 'submit_authors', - kwargs={'article_id': article_id}, + "submit_authors", + kwargs={"article_id": article_id}, ) ) - if request.POST and 'add_author' in request.POST: + if request.POST and "add_author" in request.POST: form = forms.AuthorForm(request.POST) - modal = 'author' + modal = "author" email = request.POST.get("email") author = None @@ -307,29 +304,32 @@ def submit_authors(request, article_id): author = new_author else: messages.add_message( - request, messages.WARNING, - _('Errors found in the new author form'), + request, + messages.WARNING, + _("Errors found in the new author form"), ) if author: logic.add_user_as_author(author, article) messages.add_message( - request, messages.SUCCESS, - _('{author_name} added to the article.').format( + request, + messages.SUCCESS, + _("{author_name} added to the article.").format( author_name=author.full_name(), ), ) - return redirect(reverse( - 'submit_authors', kwargs={'article_id': article_id})) + return redirect( + reverse("submit_authors", kwargs={"article_id": article_id}) + ) - elif request.POST and 'search_authors' in request.POST: - search = request.POST.get('author_search_text', None) + elif request.POST and "search_authors" in request.POST: + search = request.POST.get("author_search_text", None) if not search: messages.add_message( request, messages.WARNING, - _('An empty search is not allowed.'), + _("An empty search is not allowed."), ) else: try: @@ -340,24 +340,25 @@ def submit_authors(request, article_id): messages.add_message( request, messages.SUCCESS, - _('{author_name} added to the article.').format( + _("{author_name} added to the article.").format( author_name=search_author.full_name(), ), ) except core_models.Account.DoesNotExist: messages.add_message( - request, messages.WARNING, - _('No author found with those details.'), + request, + messages.WARNING, + _("No author found with those details."), ) - elif request.POST and 'main-author' in request.POST: - correspondence_author = request.POST.get('main-author', None) + elif request.POST and "main-author" in request.POST: + correspondence_author = request.POST.get("main-author", None) - if correspondence_author == 'None': + if correspondence_author == "None": messages.add_message( request, messages.WARNING, - _('You must select a main author.'), + _("You must select a main author."), ) else: author = core_models.Account.objects.get(pk=correspondence_author) @@ -365,20 +366,19 @@ def submit_authors(request, article_id): article.current_step = 3 article.save() - return redirect(reverse( - 'submit_files', kwargs={'article_id': article_id})) + return redirect(reverse("submit_files", kwargs={"article_id": article_id})) - elif request.POST and 'authors[]' in request.POST: + elif request.POST and "authors[]" in request.POST: logic.save_author_order(request, article) - return HttpResponse('Complete') + return HttpResponse("Complete") - template = 'admin/submission//submit_authors.html' + template = "admin/submission//submit_authors.html" context = { - 'error': error, - 'article': article, - 'form': form, - 'modal': modal, + "error": error, + "article": article, + "form": form, + "modal": modal, } return render(request, template, context) @@ -411,9 +411,7 @@ def edit_funder(request, article_id, funder_id): # If the user is not an editor/section editor/journal manager/staff # and the article is submitted we should raise PermissionDenied. if article.date_submitted and not request.user.has_an_editor_role(request): - raise PermissionDenied( - 'This article has been submitted and cannot be edited.' - ) + raise PermissionDenied("This article has been submitted and cannot be edited.") if request.POST: form = forms.ArticleFundingForm( @@ -425,12 +423,12 @@ def edit_funder(request, article_id, funder_id): messages.add_message( request, messages.SUCCESS, - 'Article funding information saved.', + "Article funding information saved.", ) # The incoming link _should_ have a return value set to ensure # the user gets back to the right place. - if request.GET.get('return'): - return redirect(request.GET['return']) + if request.GET.get("return"): + return redirect(request.GET["return"]) # If no return value is set we should try to work out where the # user should be sent to. @@ -439,27 +437,27 @@ def edit_funder(request, article_id, funder_id): # user is the owner, it's likely the user came from submission. return redirect( reverse( - 'submit_funding', + "submit_funding", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) else: return redirect( reverse( - 'edit_metadata', + "edit_metadata", kwargs={ - 'article_id': article.pk, - } + "article_id": article.pk, + }, ) ) - template = 'admin/submission/edit/funder.html' + template = "admin/submission/edit/funder.html" context = { - 'article': article, - 'funder': funder, - 'form': form, + "article": article, + "funder": funder, + "form": form, } return render( request, @@ -473,11 +471,7 @@ def edit_funder(request, article_id, funder_id): @article_edit_user_required def delete_funder(request, article_id, funder_id): """Allows submitting author to delete a funding object.""" - article = get_object_or_404( - models.Article, - pk=article_id, - journal=request.journal - ) + article = get_object_or_404(models.Article, pk=article_id, journal=request.journal) article_funding = get_object_or_404( models.ArticleFunding, pk=funder_id, @@ -486,25 +480,18 @@ def delete_funder(request, article_id, funder_id): article_funding.delete() - if request.GET.get('return'): - return redirect(request.GET['return']) + if request.GET.get("return"): + return redirect(request.GET["return"]) - return redirect(reverse('submit_funding', kwargs={'article_id': article_id})) + return redirect(reverse("submit_funding", kwargs={"article_id": article_id})) @login_required @article_edit_user_required def delete_author(request, article_id, author_id): """Allows submitting author to delete an author object.""" - article = get_object_or_404( - models.Article, - pk=article_id, - journal=request.journal - ) - author = get_object_or_404( - core_models.Account, - pk=author_id - ) + article = get_object_or_404(models.Article, pk=article_id, journal=request.journal) + author = get_object_or_404(core_models.Account, pk=author_id) article.authors.remove(author) @@ -519,7 +506,7 @@ def delete_author(request, article_id, author_id): except models.ArticleAuthorOrder.DoesNotExist: pass - return redirect(reverse('submit_authors', kwargs={'article_id': article_id})) + return redirect(reverse("submit_authors", kwargs={"article_id": article_id})) @login_required @@ -552,26 +539,27 @@ def submit_files(request, article_id): configuration = request.journal.submissionconfiguration if article.current_step < 3 and not request.user.is_staff: - return redirect(reverse('submit_authors', kwargs={'article_id': article_id})) + return redirect(reverse("submit_authors", kwargs={"article_id": article_id})) error, modal = None, None if request.POST: - - if 'delete' in request.POST: - file_id = request.POST.get('delete') - file = get_object_or_404(core_models.File, pk=file_id, article_id=article.pk) + if "delete" in request.POST: + file_id = request.POST.get("delete") + file = get_object_or_404( + core_models.File, pk=file_id, article_id=article.pk + ) file.delete() messages.add_message( request, messages.WARNING, - _('File deleted'), + _("File deleted"), ) - return redirect(reverse('submit_files', kwargs={'article_id': article_id})) + return redirect(reverse("submit_files", kwargs={"article_id": article_id})) - if 'manuscript' in request.POST: + if "manuscript" in request.POST: ms_form = forms.FileDetails(request.POST) - uploaded_file = request.FILES.get('file') + uploaded_file = request.FILES.get("file") if logic.check_file(uploaded_file, request, ms_form): if ms_form.is_valid(): new_file = files.save_file_to_article( @@ -580,20 +568,20 @@ def submit_files(request, article_id): request.user, ) article.manuscript_files.add(new_file) - new_file.label = ms_form.cleaned_data['label'] - new_file.description = ms_form.cleaned_data['description'] + new_file.label = ms_form.cleaned_data["label"] + new_file.description = ms_form.cleaned_data["description"] new_file.save() return redirect( - reverse('submit_files', kwargs={'article_id': article_id}), + reverse("submit_files", kwargs={"article_id": article_id}), ) else: - modal = 'manuscript' + modal = "manuscript" else: - modal = 'manuscript' + modal = "manuscript" - if 'data' in request.POST: + if "data" in request.POST: data_form = forms.FileDetails(request.POST) - uploaded_file = request.FILES.get('file') + uploaded_file = request.FILES.get("file") if data_form.is_valid() and uploaded_file: new_file = files.save_file_to_article( uploaded_file, @@ -601,35 +589,39 @@ def submit_files(request, article_id): request.user, ) article.data_figure_files.add(new_file) - new_file.label = data_form.cleaned_data['label'] - new_file.description = data_form.cleaned_data['description'] + new_file.label = data_form.cleaned_data["label"] + new_file.description = data_form.cleaned_data["description"] new_file.save() - return redirect(reverse('submit_files', kwargs={'article_id': article_id})) + return redirect( + reverse("submit_files", kwargs={"article_id": article_id}) + ) else: - data_form.add_error(None, 'You must select a file.') - modal = 'data' + data_form.add_error(None, "You must select a file.") + modal = "data" - if 'next_step' in request.POST: + if "next_step" in request.POST: if article.manuscript_files.all().count() >= 1: article.current_step = 4 article.save() if configuration.funding: - return redirect(reverse( - 'submit_funding', kwargs={'article_id': article_id})) + return redirect( + reverse("submit_funding", kwargs={"article_id": article_id}) + ) else: - return redirect(reverse( - 'submit_review', kwargs={'article_id': article_id})) + return redirect( + reverse("submit_review", kwargs={"article_id": article_id}) + ) else: error = _("You must upload a manuscript file.") template = "admin/submission/submit_files.html" context = { - 'article': article, - 'error': error, - 'ms_form': ms_form, - 'data_form': data_form, - 'modal': modal, + "article": article, + "error": error, + "ms_form": ms_form, + "data_form": data_form, + "modal": modal, } return render(request, template, context) @@ -656,15 +648,15 @@ def submit_review(request, article_id): if article.current_step < 4 and not request.user.is_staff: return redirect( reverse( - 'submit_info', - kwargs={'article_id': article_id}, + "submit_info", + kwargs={"article_id": article_id}, ) ) form = forms.SubmissionCommentsForm( instance=article, ) - if request.POST and 'next_step' in request.POST: + if request.POST and "next_step" in request.POST: form = forms.SubmissionCommentsForm( request.POST, instance=article, @@ -679,34 +671,33 @@ def submit_review(request, article_id): event_logic.Events.raise_event( event_logic.Events.ON_WORKFLOW_ELEMENT_COMPLETE, - **{'handshake_url': 'submit_review', - 'request': request, - 'article': article, - 'switch_stage': False} + **{ + "handshake_url": "submit_review", + "request": request, + "article": article, + "switch_stage": False, + }, ) messages.add_message( request, messages.SUCCESS, - _('Article {title} submitted').format( + _("Article {title} submitted").format( title=article.title, ), ) - kwargs = {'article': article, - 'request': request} + kwargs = {"article": article, "request": request} event_logic.Events.raise_event( - event_logic.Events.ON_ARTICLE_SUBMITTED, - task_object=article, - **kwargs + event_logic.Events.ON_ARTICLE_SUBMITTED, task_object=article, **kwargs ) - return redirect(reverse('core_dashboard')) + return redirect(reverse("core_dashboard")) template = "admin/submission/submit_review.html" context = { - 'article': article, - 'form': form, + "article": article, + "form": form, } return render(request, template, context) @@ -731,8 +722,8 @@ def edit_metadata(request, article_id): journal=request.journal, ) submission_summary = setting_handler.get_setting( - 'general', - 'submission_summary', + "general", + "submission_summary", request.journal, ).processed_value funder_form = forms.ArticleFundingForm( @@ -747,23 +738,23 @@ def edit_metadata(request, article_id): editor_view=True, ) - return_param = request.GET.get('return') + return_param = request.GET.get("return") reverse_url = create_language_override_redirect( request, - 'edit_metadata', - {'article_id': article.pk}, - query_strings={'return': return_param} + "edit_metadata", + {"article_id": article.pk}, + query_strings={"return": return_param}, ) frozen_author, modal, author_form = None, None, forms.EditFrozenAuthor() - if request.GET.get('author'): + if request.GET.get("author"): frozen_author, modal = logic.get_author(request, article) author_form = forms.EditFrozenAuthor(instance=frozen_author) - elif request.GET.get('modal') == 'author': - modal = 'author' + elif request.GET.get("modal") == "author": + modal = "author" if request.POST: - if 'add_funder' in request.POST: + if "add_funder" in request.POST: funder_form = forms.ArticleFundingForm( request.POST, article=article, @@ -772,7 +763,7 @@ def edit_metadata(request, article_id): funder_form.save() return redirect(reverse_url) - if 'metadata' in request.POST: + if "metadata" in request.POST: info_form = forms.EditArticleMetadata( request.POST, instance=article, @@ -787,23 +778,21 @@ def edit_metadata(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Metadata updated.'), + _("Metadata updated."), ) return redirect(reverse_url) - if 'mark_primary' in request.POST and frozen_author.author: + if "mark_primary" in request.POST and frozen_author.author: article.correspondence_author = frozen_author.author article.save() frozen_author.display_email = True frozen_author.save() messages.add_message( - request, - messages.SUCCESS, - _('Primary author set.') + request, messages.SUCCESS, _("Primary author set.") ) return redirect(reverse_url) - if 'author' in request.POST: + if "author" in request.POST: author_form = forms.EditFrozenAuthor( request.POST, instance=frozen_author, @@ -816,14 +805,14 @@ def edit_metadata(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Author {author_name} updated.').format( + _("Author {author_name} updated.").format( author_name=saved_author.full_name(), ), ) return redirect(reverse_url) - if 'delete' in request.POST: - frozen_author_id = request.POST.get('delete') + if "delete" in request.POST: + frozen_author_id = request.POST.get("delete") frozen_author = get_object_or_404( models.FrozenAuthor, pk=frozen_author_id, @@ -834,20 +823,20 @@ def edit_metadata(request, article_id): messages.add_message( request, messages.SUCCESS, - _('Frozen Author deleted.'), + _("Frozen Author deleted."), ) return redirect(reverse_url) - template = 'submission/edit/metadata.html' + template = "submission/edit/metadata.html" context = { - 'article': article, - 'funder_form': funder_form, - 'info_form': info_form, - 'author_form': author_form, - 'modal': modal, - 'frozen_author': frozen_author, - 'additional_fields': additional_fields, - 'return': return_param + "article": article, + "funder_form": funder_form, + "info_form": info_form, + "author_form": author_form, + "modal": modal, + "frozen_author": frozen_author, + "additional_fields": additional_fields, + "return": return_param, } return render(request, template, context) @@ -858,14 +847,14 @@ def order_authors(request, article_id): article = get_object_or_404(models.Article, pk=article_id, journal=request.journal) if request.POST: - ids = [int(_id) for _id in request.POST.getlist('authors[]')] + ids = [int(_id) for _id in request.POST.getlist("authors[]")] for author in article.frozenauthor_set.all(): order = ids.index(author.pk) author.order = order author.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") @editor_user_required @@ -882,33 +871,32 @@ def fields(request, field_id=None): form = forms.FieldForm(instance=field) if request.POST: - - if 'save' in request.POST: + if "save" in request.POST: form = forms.FieldForm(request.POST, instance=field) if form.is_valid(): logic.save_field(request, form) - return redirect(reverse('submission_fields')) + return redirect(reverse("submission_fields")) - elif 'delete' in request.POST: + elif "delete" in request.POST: logic.delete_field(request) - return redirect(reverse('submission_fields')) + return redirect(reverse("submission_fields")) - elif 'order[]' in request.POST: + elif "order[]" in request.POST: logic.order_fields(request, fields) - return HttpResponse('Thanks') + return HttpResponse("Thanks") - template = 'admin/submission/manager/fields.html' + template = "admin/submission/manager/fields.html" context = { - 'field': field, - 'fields': fields, - 'form': form, + "field": field, + "fields": fields, + "form": form, } return render(request, template, context) -@role_can_access('licenses') +@role_can_access("licenses") def licenses(request, license_pk=None): """ Allows an editor to create, edit and delete license objects. @@ -921,22 +909,17 @@ def licenses(request, license_pk=None): if license_pk and request.journal: license_obj = get_object_or_404( - models.Licence, - journal=request.journal, - pk=license_pk + models.Licence, journal=request.journal, pk=license_pk ) elif license_pk and request.press: license_obj = get_object_or_404( - models.Licence, - press=request.press, - pk=license_pk + models.Licence, press=request.press, pk=license_pk ) form = forms.LicenseForm(instance=license_obj) - if request.POST and 'save' in request.POST: - form = forms.LicenseForm(request.POST, - instance=license_obj) + if request.POST and "save" in request.POST: + form = forms.LicenseForm(request.POST, instance=license_obj) if form.is_valid(): save_license = form.save(commit=False) @@ -949,31 +932,31 @@ def licenses(request, license_pk=None): messages.add_message( request, messages.INFO, - _('License saved.'), + _("License saved."), ) - return redirect(reverse('submission_licenses')) + return redirect(reverse("submission_licenses")) - elif 'order[]' in request.POST: - ids = [int(_id) for _id in request.POST.getlist('order[]')] + elif "order[]" in request.POST: + ids = [int(_id) for _id in request.POST.getlist("order[]")] for license in licenses: order = ids.index(license.pk) license.order = order license.save() - return HttpResponse('Thanks') + return HttpResponse("Thanks") - template = 'submission/manager/licenses.html' + template = "submission/manager/licenses.html" context = { - 'license': license_obj, - 'form': form, - 'licenses': licenses, + "license": license_obj, + "form": form, + "licenses": licenses, } return render(request, template, context) -@role_can_access('licenses') +@role_can_access("licenses") def delete_license(request, license_pk): """ Presents an interface to delete a license object. @@ -982,30 +965,26 @@ def delete_license(request, license_pk): :return: HttpResponse or HttpRedirect """ license_to_delete = get_object_or_404( - models.Licence, - pk=license_pk, - journal=request.journal - ) - license_articles = models.Article.objects.filter( - license=license_to_delete + models.Licence, pk=license_pk, journal=request.journal ) + license_articles = models.Article.objects.filter(license=license_to_delete) - if request.POST and 'delete' in request.POST: + if request.POST and "delete" in request.POST: messages.add_message( request, messages.INFO, - _('License {license_name} deleted.').format( + _("License {license_name} deleted.").format( license_name=license_to_delete.name, ), ) license_to_delete.delete() - return redirect(reverse('submission_licenses')) + return redirect(reverse("submission_licenses")) - template = 'submission/manager/delete_license.html' + template = "submission/manager/delete_license.html" context = { - 'license': license_to_delete, - 'license_articles': license_articles, + "license": license_to_delete, + "license_articles": license_articles, } return render(request, template, context) @@ -1030,14 +1009,14 @@ def configurator(request): messages.add_message( request, messages.INFO, - _('Configuration updated.'), + _("Configuration updated."), ) - return redirect(reverse('submission_configurator')) + return redirect(reverse("submission_configurator")) - template = 'submission/manager/configurator.html' + template = "submission/manager/configurator.html" context = { - 'configuration': configuration, - 'form': form, + "configuration": configuration, + "form": form, } return render(request, template, context) diff --git a/src/themes/OLH/build_assets.py b/src/themes/OLH/build_assets.py index e8e83f0601..b988562e7d 100755 --- a/src/themes/OLH/build_assets.py +++ b/src/themes/OLH/build_assets.py @@ -12,18 +12,18 @@ def process_scss(): """Compiles SCSS into CSS in the Static Assets folder""" paths = [ - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/foundation-sites/scss/'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/motion-ui/src/') + os.path.join(settings.BASE_DIR, "themes/OLH/assets/foundation-sites/scss/"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/motion-ui/src/"), ] # File dirs - app_scss_file = os.path.join(settings.BASE_DIR, 'themes/OLH/assets/scss/app.scss') - app_css_file = os.path.join(settings.BASE_DIR, 'static/OLH/css/app.css') + app_scss_file = os.path.join(settings.BASE_DIR, "themes/OLH/assets/scss/app.scss") + app_css_file = os.path.join(settings.BASE_DIR, "static/OLH/css/app.css") compiled_css_from_file = sass.compile(filename=app_scss_file, include_paths=paths) # Open the CSS file and write into it - write_file = open(app_css_file, 'w', encoding="utf-8") + write_file = open(app_css_file, "w", encoding="utf-8") write_file.write(compiled_css_from_file) @@ -36,10 +36,10 @@ def minify_js_proc(src_text): def process_js_files(source_paths, dest_path, min_path): - f = open(dest_path, 'w', encoding="utf-8") + f = open(dest_path, "w", encoding="utf-8") js_file = None try: - js_file = open(min_path, 'w', encoding="utf-8") + js_file = open(min_path, "w", encoding="utf-8") for src_file in source_paths: with open(src_file, "r", encoding="utf-8") as inputFile: src_text = inputFile.read() @@ -53,18 +53,17 @@ def process_js_files(source_paths, dest_path, min_path): def process_js(): - """Copies JS from compile into static assets - """ + """Copies JS from compile into static assets""" source_paths = [ - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/admin.js'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/app.js'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/footnotes.js'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/table_of_contents.js'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/text_resize.js'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/js/toastr.js'), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/admin.js"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/app.js"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/footnotes.js"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/table_of_contents.js"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/text_resize.js"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/js/toastr.js"), ] - dest_path = os.path.join(settings.BASE_DIR, 'static/OLH/js/app.js') - min_path = os.path.join(settings.BASE_DIR, 'static/OLH/js/app.min.js') + dest_path = os.path.join(settings.BASE_DIR, "static/OLH/js/app.js") + min_path = os.path.join(settings.BASE_DIR, "static/OLH/js/app.min.js") process_js_files(source_paths, dest_path, min_path) @@ -103,22 +102,24 @@ def copy_file(source, destination): if not os.path.exists(destination_folder): os.mkdir(destination_folder) - shutil.copy(os.path.join(settings.BASE_DIR, source), - os.path.join(settings.BASE_DIR, destination)) + shutil.copy( + os.path.join(settings.BASE_DIR, source), + os.path.join(settings.BASE_DIR, destination), + ) def process_fonts(): """Processes fonts from the compile folder into Static Assets""" - fonts_path = os.path.join(settings.BASE_DIR, 'themes/OLH/assets/fonts/') - static_fonts = os.path.join(settings.BASE_DIR, 'static/OLH/fonts/') + fonts_path = os.path.join(settings.BASE_DIR, "themes/OLH/assets/fonts/") + static_fonts = os.path.join(settings.BASE_DIR, "static/OLH/fonts/") copy_files(fonts_path, static_fonts) def process_images(): """Processes images from the compile folder into Static Assets""" - image_path = os.path.join(settings.BASE_DIR, 'themes/OLH/assets/img/') - static_images = os.path.join(settings.BASE_DIR, 'static/OLH/img/') + image_path = os.path.join(settings.BASE_DIR, "themes/OLH/assets/img/") + static_images = os.path.join(settings.BASE_DIR, "static/OLH/img/") copy_files(image_path, static_images) @@ -131,40 +132,63 @@ def process_journals(override_css_dir, paths): scss_files = journal.scss_files if len(scss_files) == 0: - print('Journal with ID {0} [{1}] has no SCSS to compile'.format(journal.id, journal.name)) + print( + "Journal with ID {0} [{1}] has no SCSS to compile".format( + journal.id, journal.name + ) + ) else: - print('Journal with ID {0} [{1}]: processing overrides'.format(journal.id, journal.name)) + print( + "Journal with ID {0} [{1}]: processing overrides".format( + journal.id, journal.name + ) + ) - override_css_file = os.path.join(override_css_dir, 'journal{0}_override.css'.format(str(journal.id))) + override_css_file = os.path.join( + override_css_dir, "journal{0}_override.css".format(str(journal.id)) + ) # we will only process one single SCSS override file for a journal - compiled_css_from_file = sass.compile(filename=scss_files[0], include_paths=paths) + compiled_css_from_file = sass.compile( + filename=scss_files[0], include_paths=paths + ) # open the journal CSS override file and write into it - with open(override_css_file, 'w', encoding="utf-8") as write_file: + with open(override_css_file, "w", encoding="utf-8") as write_file: write_file.write(compiled_css_from_file) - journal_dir = os.path.join(settings.BASE_DIR, 'files', 'journals', str(journal.id)) - journal_header_image = os.path.join(journal_dir, 'header.png') + journal_dir = os.path.join( + settings.BASE_DIR, "files", "journals", str(journal.id) + ) + journal_header_image = os.path.join(journal_dir, "header.png") if os.path.isfile(journal_header_image): - print('Journal with ID {0} [{1}]: processing header image'.format(journal.id, journal.name)) - dest_path = os.path.join(settings.BASE_DIR, 'static', 'OLH', 'img', 'journal_header{0}.png'.format(journal.id)) + print( + "Journal with ID {0} [{1}]: processing header image".format( + journal.id, journal.name + ) + ) + dest_path = os.path.join( + settings.BASE_DIR, + "static", + "OLH", + "img", + "journal_header{0}.png".format(journal.id), + ) copy_file(journal_header_image, dest_path) def process_default_override(override_css_dir, include_paths): scss_default_override = os.path.join( - settings.BASE_DIR, 'files', 'styling', - 'journals', 'default', 'override.scss' + settings.BASE_DIR, "files", "styling", "journals", "default", "override.scss" ) - override_css_file = os.path.join(override_css_dir, 'default_override.css') + override_css_file = os.path.join(override_css_dir, "default_override.css") if os.path.isfile(scss_default_override): compiled = sass.compile( - filename=scss_default_override, - include_paths=include_paths, + filename=scss_default_override, + include_paths=include_paths, ) with open(override_css_file, "w", encoding="utf-8") as f: f.write(compiled) @@ -172,28 +196,28 @@ def process_default_override(override_css_dir, include_paths): def process_press_override(override_css_dir, include_paths): scss_default_override = os.path.join( - settings.BASE_DIR, 'files', 'styling', - 'press', 'override.scss' + settings.BASE_DIR, "files", "styling", "press", "override.scss" ) - override_css_file = os.path.join(override_css_dir, 'press_override.css') + override_css_file = os.path.join(override_css_dir, "press_override.css") if os.path.isfile(scss_default_override): compiled = sass.compile( - filename=scss_default_override, - include_paths=include_paths, + filename=scss_default_override, + include_paths=include_paths, ) with open(override_css_file, "w", encoding="utf-8") as f: f.write(compiled) + def create_paths(): - base_path = os.path.join(settings.BASE_DIR, 'static', 'OLH') - folders = ['css', 'js', 'fonts', 'img'] + base_path = os.path.join(settings.BASE_DIR, "static", "OLH") + folders = ["css", "js", "fonts", "img"] for folder in folders: os.makedirs(os.path.join(base_path, folder), exist_ok=True) # test if the journal CSS directory exists and create it if not - override_css_dir = os.path.join(settings.BASE_DIR, 'static', 'OLH', 'css') + override_css_dir = os.path.join(settings.BASE_DIR, "static", "OLH", "css") os.makedirs(override_css_dir, exist_ok=True) return override_css_dir @@ -207,10 +231,10 @@ def build(): process_js() print("Processing journal overrides") include_paths = [ - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/foundation-sites/scss/'), - os.path.join(settings.BASE_DIR, 'themes/OLH/assets/motion-ui/src/') + os.path.join(settings.BASE_DIR, "themes/OLH/assets/foundation-sites/scss/"), + os.path.join(settings.BASE_DIR, "themes/OLH/assets/motion-ui/src/"), ] process_default_override(override_css_dir, include_paths) process_journals(override_css_dir, include_paths) process_press_override(override_css_dir, include_paths) - call_command('collectstatic', '--noinput') + call_command("collectstatic", "--noinput") diff --git a/src/themes/clean/build_assets.py b/src/themes/clean/build_assets.py index 210ac43b29..c78ed87896 100644 --- a/src/themes/clean/build_assets.py +++ b/src/themes/clean/build_assets.py @@ -18,13 +18,15 @@ def copy_file(source, destination): if not os.path.exists(destination_folder): os.mkdir(destination_folder) - shutil.copy(os.path.join(settings.BASE_DIR, source), - os.path.join(settings.BASE_DIR, destination)) + shutil.copy( + os.path.join(settings.BASE_DIR, source), + os.path.join(settings.BASE_DIR, destination), + ) def create_paths(): - base_path = os.path.join(settings.BASE_DIR, 'static', 'clean') - folders = ['css'] + base_path = os.path.join(settings.BASE_DIR, "static", "clean") + folders = ["css"] for folder in folders: os.makedirs(os.path.join(base_path, folder), exist_ok=True) @@ -35,10 +37,18 @@ def process_journals(): for journal in journals: for file in journal.scss_files: - if file.endswith('clean_override.css'): - print('Copying material override file for {name}'.format(name=journal.name)) - override_css_dir = os.path.join(settings.BASE_DIR, 'static', 'clean', 'css') - override_css_file = os.path.join(override_css_dir, 'journal{0}_override.css'.format(str(journal.id))) + if file.endswith("clean_override.css"): + print( + "Copying material override file for {name}".format( + name=journal.name + ) + ) + override_css_dir = os.path.join( + settings.BASE_DIR, "static", "clean", "css" + ) + override_css_file = os.path.join( + override_css_dir, "journal{0}_override.css".format(str(journal.id)) + ) # test if the journal CSS directory exists and create it if not os.makedirs(override_css_dir, exist_ok=True) @@ -48,8 +58,8 @@ def process_journals(): def build(): - print('Creating folders') + print("Creating folders") create_paths() - print('Copying CSS') - copy_file('themes/clean/assets/css/clean.css', 'static/clean/css/clean.css') + print("Copying CSS") + copy_file("themes/clean/assets/css/clean.css", "static/clean/css/clean.css") process_journals() diff --git a/src/themes/material/build_assets.py b/src/themes/material/build_assets.py index 74a0460c5e..cbe8075077 100644 --- a/src/themes/material/build_assets.py +++ b/src/themes/material/build_assets.py @@ -8,9 +8,13 @@ def press_scss_files(): try: - scss_path = os.path.join(settings.BASE_DIR, 'files', 'styling', 'press') + scss_path = os.path.join(settings.BASE_DIR, "files", "styling", "press") os.makedirs(scss_path, exist_ok=True) - return [os.path.join(scss_path, f) for f in os.listdir(scss_path) if os.path.isfile(os.path.join(scss_path, f))] + return [ + os.path.join(scss_path, f) + for f in os.listdir(scss_path) + if os.path.isfile(os.path.join(scss_path, f)) + ] except FileNotFoundError: return [] @@ -27,8 +31,10 @@ def copy_file(source, destination): if not os.path.exists(destination_folder): os.mkdir(destination_folder) - shutil.copy(os.path.join(settings.BASE_DIR, source), - os.path.join(settings.BASE_DIR, destination)) + shutil.copy( + os.path.join(settings.BASE_DIR, source), + os.path.join(settings.BASE_DIR, destination), + ) def process_journals(): @@ -36,10 +42,18 @@ def process_journals(): for journal in journals: for file in journal.scss_files: - if file.endswith('material_override.css'): - print('Copying material override file for {name}'.format(name=journal.name)) - override_css_dir = os.path.join(settings.BASE_DIR, 'static', 'material', 'css') - override_css_file = os.path.join(override_css_dir, 'journal{0}_override.css'.format(str(journal.id))) + if file.endswith("material_override.css"): + print( + "Copying material override file for {name}".format( + name=journal.name + ) + ) + override_css_dir = os.path.join( + settings.BASE_DIR, "static", "material", "css" + ) + override_css_file = os.path.join( + override_css_dir, "journal{0}_override.css".format(str(journal.id)) + ) # test if the journal CSS directory exists and create it if not os.makedirs(override_css_dir, exist_ok=True) @@ -50,10 +64,12 @@ def process_journals(): def process_press(): for file in press_scss_files(): - if file.endswith('material_override.css'): - print('Copying material override file for press') - override_css_dir = os.path.join(settings.BASE_DIR, 'static', 'material', 'css') - override_css_file = os.path.join(override_css_dir, 'press_override.css') + if file.endswith("material_override.css"): + print("Copying material override file for press") + override_css_dir = os.path.join( + settings.BASE_DIR, "static", "material", "css" + ) + override_css_file = os.path.join(override_css_dir, "press_override.css") # test if the journal CSS directory exists and create it if not os.makedirs(override_css_dir, exist_ok=True) @@ -63,10 +79,10 @@ def process_press(): def build(): - print('Copying Material Theme CSS') - copy_file('themes/material/assets/material.js', 'static/material/material.js') - copy_file('themes/material/assets/toc.js', 'static/material/toc.js') - copy_file('themes/material/assets/sub-toc.js', 'static/material/sub-toc.js') - copy_file('themes/material/assets/mat.css', 'static/material/mat.css') + print("Copying Material Theme CSS") + copy_file("themes/material/assets/material.js", "static/material/material.js") + copy_file("themes/material/assets/toc.js", "static/material/toc.js") + copy_file("themes/material/assets/sub-toc.js", "static/material/sub-toc.js") + copy_file("themes/material/assets/mat.css", "static/material/mat.css") process_journals() process_press() diff --git a/src/transform/cassius/cassius-import/bin/cassius-import.py b/src/transform/cassius/cassius-import/bin/cassius-import.py index 716767f6ee..fab1c69d0f 100755 --- a/src/transform/cassius/cassius-import/bin/cassius-import.py +++ b/src/transform/cassius/cassius-import/bin/cassius-import.py @@ -15,7 +15,6 @@ --version Show version. """ - import os from os import listdir from os.path import isfile, join @@ -26,7 +25,7 @@ import subprocess -class CassiusImport (Debuggable): +class CassiusImport(Debuggable): def __init__(self): # read command line arguments self.args = self.read_command_line() @@ -34,29 +33,31 @@ def __init__(self): # absolute first priority is to initialize debugger so that anything triggered here can be logged self.debug = Debug() - Debuggable.__init__(self, 'cassius-import') + Debuggable.__init__(self, "cassius-import") - self.in_file = self.args[''] - self.out_file = self.args[''] + self.in_file = self.args[""] + self.out_file = self.args[""] self.dir = os.path.dirname(os.path.abspath(__file__)) - if self.args['--debug']: + if self.args["--debug"]: self.debug.enable_debug() - self.debug.enable_prompt(Interactive(self.args['--debug'])) + self.debug.enable_prompt(Interactive(self.args["--debug"])) @staticmethod def read_command_line(): - return docopt(__doc__, version='cassius-import v0.1') + return docopt(__doc__, version="cassius-import v0.1") def run(self): - command = "java -cp '{0}{1}saxon9.jar':'{0}{1}..{1}runtime{1}xml-resolver-1.1.jar':'{0}{1}..{1}runtime{1}' net.sf.saxon.Transform -r:org.apache.xml.resolver.tools.CatalogResolver -y:org.apache.xml.resolver.tools.ResolvingXMLReader -x:org.apache.xml.resolver.tools.ResolvingXMLReader -u -o '{2}' '{3}' '{0}{1}..{1}transform{1}xsl{1}cassius-main.xsl'".format(self.dir, os.sep, self.out_file, self.in_file) - #command = "java -jar '{0}{1}saxon9.jar';'{0}{1}..{1}runtime{1}xml-resolver-1.1.jar' -o '{2}' '{3}' '{0}{1}..{1}transform{1}xsl{1}cassius-main.xsl'".format(self.dir, os.sep, self.out_file, self.in_file) + command = "java -cp '{0}{1}saxon9.jar':'{0}{1}..{1}runtime{1}xml-resolver-1.1.jar':'{0}{1}..{1}runtime{1}' net.sf.saxon.Transform -r:org.apache.xml.resolver.tools.CatalogResolver -y:org.apache.xml.resolver.tools.ResolvingXMLReader -x:org.apache.xml.resolver.tools.ResolvingXMLReader -u -o '{2}' '{3}' '{0}{1}..{1}transform{1}xsl{1}cassius-main.xsl'".format( + self.dir, os.sep, self.out_file, self.in_file + ) + # command = "java -jar '{0}{1}saxon9.jar';'{0}{1}..{1}runtime{1}xml-resolver-1.1.jar' -o '{2}' '{3}' '{0}{1}..{1}transform{1}xsl{1}cassius-main.xsl'".format(self.dir, os.sep, self.out_file, self.in_file) # -r org.apache.xml.resolver.tools.CatalogResolver -catalog '{0}{1}..{1}runtime{1}catalog.xml' - self.debug.print_debug(self, u'Running saxon transform (JATS -> CaSSius)') + self.debug.print_debug(self, "Running saxon transform (JATS -> CaSSius)") subprocess.call(command, stdin=None, shell=True) @@ -66,5 +67,5 @@ def main(): cwf_instance.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/transform/cassius/cassius-import/bin/debug.py b/src/transform/cassius/cassius-import/bin/debug.py index 25207f8859..ef0f651b6e 100755 --- a/src/transform/cassius/cassius-import/bin/debug.py +++ b/src/transform/cassius/cassius-import/bin/debug.py @@ -1,6 +1,6 @@ from __future__ import print_function -__author__ = 'Martin Paul Eve' +__author__ = "Martin Paul Eve" __email__ = "martin@martineve.com" import sys @@ -25,13 +25,17 @@ def enable_prompt(self, prompt): def print_(self, module, message): if self.prompt is None: - print(u'[{0}] {1}'.format(module.get_module_name(), unicode(message))) + print("[{0}] {1}".format(module.get_module_name(), unicode(message))) else: - self.prompt.print_(u'[{0}] {1}'.format(self.prompt.colorize('red', module.get_module_name()), - unicode(message))) + self.prompt.print_( + "[{0}] {1}".format( + self.prompt.colorize("red", module.get_module_name()), + unicode(message), + ) + ) def get_module_name(self): - return 'Debugger' + return "Debugger" def print_debug(self, module, message): """ @@ -40,9 +44,10 @@ def print_debug(self, module, message): @param message: the debug message to print """ if self.debug: - if not isinstance(message, unicode): - self.fatal_error(self, u'A non unicode string was passed to the debugger') + self.fatal_error( + self, "A non unicode string was passed to the debugger" + ) self.print_(module, message) @@ -57,13 +62,13 @@ def write_error(self, module, message, error_number): module.gv.mk_dir(module.gv.error_folder_path) self.has_run = True - error_file = open(module.gv.error_file_path, 'w') - print(u'[{0}] {1}\n'.format(error_number, message), file=error_file) + error_file = open(module.gv.error_file_path, "w") + print("[{0}] {1}\n".format(error_number, message), file=error_file) error_file.close() @staticmethod def fatal_error(module, message): - print(u'[FATAL ERROR] [{0}] {1}'.format(module.get_module_name(), message)) + print("[FATAL ERROR] [{0}] {1}".format(module.get_module_name(), message)) sys.exit(1) @@ -72,4 +77,4 @@ def __init__(self, module_name): self.module_name = module_name def get_module_name(self): - return unicode(self.module_name, 'utf-8') + return unicode(self.module_name, "utf-8") diff --git a/src/transform/cassius/cassius-import/bin/docopt.py b/src/transform/cassius/cassius-import/bin/docopt.py index 559587db74..109605a31d 100755 --- a/src/transform/cassius/cassius-import/bin/docopt.py +++ b/src/transform/cassius/cassius-import/bin/docopt.py @@ -1,36 +1,34 @@ """Pythonic command-line interface parser that will make you smile. - * http://docopt.org - * Repository and issue-tracker: https://github.com/docopt/docopt - * Licensed under terms of MIT license (see LICENSE-MIT) - * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com +* http://docopt.org +* Repository and issue-tracker: https://github.com/docopt/docopt +* Licensed under terms of MIT license (see LICENSE-MIT) +* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com """ + import sys import re -__all__ = ['docopt'] -__version__ = '0.6.1' +__all__ = ["docopt"] +__version__ = "0.6.1" class DocoptLanguageError(Exception): - """Error in construction of usage-message by developer.""" class DocoptExit(SystemExit): - """Exit in case user invoked program with incorrect arguments.""" - usage = '' + usage = "" - def __init__(self, message=''): - SystemExit.__init__(self, (message + '\n' + self.usage).strip()) + def __init__(self, message=""): + SystemExit.__init__(self, (message + "\n" + self.usage).strip()) class Pattern(object): - def __eq__(self, other): return repr(self) == repr(other) @@ -44,11 +42,11 @@ def fix(self): def fix_identities(self, uniq=None): """Make pattern-tree tips point to same object if they are equal.""" - if not hasattr(self, 'children'): + if not hasattr(self, "children"): return self uniq = list(set(self.flat())) if uniq is None else uniq for i, child in enumerate(self.children): - if not hasattr(child, 'children'): + if not hasattr(child, "children"): assert child in uniq self.children[i] = uniq[uniq.index(child)] else: @@ -97,14 +95,13 @@ def transform(pattern): class LeafPattern(Pattern): - """Leaf/terminal node of a pattern tree.""" def __init__(self, name, value=None): self.name, self.value = name, value def __repr__(self): - return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) + return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.value) def flat(self, *types): return [self] if not types or type(self) in types else [] @@ -114,14 +111,15 @@ def match(self, left, collected=None): pos, match = self.single_match(left) if match is None: return False, left, collected - left_ = left[:pos] + left[pos + 1:] + left_ = left[:pos] + left[pos + 1 :] same_name = [a for a in collected if a.name == self.name] if type(self.value) in (int, list): if isinstance(self.value, int): increment = 1 else: - increment = ([match.value] if isinstance(match.value, str) - else match.value) + increment = ( + [match.value] if isinstance(match.value, str) else match.value + ) if not same_name: match.value = increment return True, left_, collected + [match] @@ -131,15 +129,16 @@ def match(self, left, collected=None): class BranchPattern(Pattern): - """Branch/inner node of a pattern tree.""" def __init__(self, *children): self.children = list(children) def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, - ', '.join(repr(a) for a in self.children)) + return "%s(%s)" % ( + self.__class__.__name__, + ", ".join(repr(a) for a in self.children), + ) def flat(self, *types): if type(self) in types: @@ -148,7 +147,6 @@ def flat(self, *types): class Argument(LeafPattern): - def single_match(self, left): for n, pattern in enumerate(left): if isinstance(pattern, Argument): @@ -157,13 +155,12 @@ def single_match(self, left): @classmethod def parse(class_, source): - name = re.findall('(<\S*?>)', source)[0] - value = re.findall('\[default: (.*)\]', source, flags=re.I) + name = re.findall("(<\S*?>)", source)[0] + value = re.findall("\[default: (.*)\]", source, flags=re.I) return class_(name, value[0] if value else None) class Command(Argument): - def __init__(self, name, value=False): self.name, self.value = name, value @@ -178,7 +175,6 @@ def single_match(self, left): class Option(LeafPattern): - def __init__(self, short=None, long=None, argcount=0, value=False): assert argcount in (0, 1) self.short, self.long, self.argcount = short, long, argcount @@ -187,17 +183,17 @@ def __init__(self, short=None, long=None, argcount=0, value=False): @classmethod def parse(class_, option_description): short, long, argcount, value = None, None, 0, False - options, _, description = option_description.strip().partition(' ') - options = options.replace(',', ' ').replace('=', ' ') + options, _, description = option_description.strip().partition(" ") + options = options.replace(",", " ").replace("=", " ") for s in options.split(): - if s.startswith('--'): + if s.startswith("--"): long = s - elif s.startswith('-'): + elif s.startswith("-"): short = s else: argcount = 1 if argcount: - matched = re.findall('\[default: (.*)\]', description, flags=re.I) + matched = re.findall("\[default: (.*)\]", description, flags=re.I) value = matched[0] if matched else None return class_(short, long, argcount, value) @@ -212,12 +208,15 @@ def name(self): return self.long or self.short def __repr__(self): - return 'Option(%r, %r, %r, %r)' % (self.short, self.long, - self.argcount, self.value) + return "Option(%r, %r, %r, %r)" % ( + self.short, + self.long, + self.argcount, + self.value, + ) class Required(BranchPattern): - def match(self, left, collected=None): collected = [] if collected is None else collected l = left @@ -230,7 +229,6 @@ def match(self, left, collected=None): class Optional(BranchPattern): - def match(self, left, collected=None): collected = [] if collected is None else collected for pattern in self.children: @@ -239,12 +237,10 @@ def match(self, left, collected=None): class OptionsShortcut(Optional): - """Marker/placeholder for [options] shortcut.""" class OneOrMore(BranchPattern): - def match(self, left, collected=None): assert len(self.children) == 1 collected = [] if collected is None else collected @@ -266,7 +262,6 @@ def match(self, left, collected=None): class Either(BranchPattern): - def match(self, left, collected=None): collected = [] if collected is None else collected outcomes = [] @@ -280,15 +275,14 @@ def match(self, left, collected=None): class Tokens(list): - def __init__(self, source, error=DocoptExit): - self += source.split() if hasattr(source, 'split') else source + self += source.split() if hasattr(source, "split") else source self.error = error @staticmethod def from_pattern(source): - source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) - source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] + source = re.sub(r"([\[\]\(\)\|]|\.\.\.)", r" \1 ", source) + source = [s for s in re.split("\s+|(\S*<.*?>)", source) if s] return Tokens(source, error=DocoptLanguageError) def move(self): @@ -300,31 +294,34 @@ def current(self): def parse_long(tokens, options): """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" - long, eq, value = tokens.move().partition('=') - assert long.startswith('--') - value = None if eq == value == '' else value + long, eq, value = tokens.move().partition("=") + assert long.startswith("--") + value = None if eq == value == "" else value similar = [o for o in options if o.long == long] if tokens.error is DocoptExit and similar == []: # if no exact match similar = [o for o in options if o.long and o.long.startswith(long)] if len(similar) > 1: # might be simply specified ambiguously 2+ times? - raise tokens.error('%s is not a unique prefix: %s?' % - (long, ', '.join(o.long for o in similar))) + raise tokens.error( + "%s is not a unique prefix: %s?" + % (long, ", ".join(o.long for o in similar)) + ) elif len(similar) < 1: - argcount = 1 if eq == '=' else 0 + argcount = 1 if eq == "=" else 0 o = Option(None, long, argcount) options.append(o) if tokens.error is DocoptExit: o = Option(None, long, argcount, value if argcount else True) else: - o = Option(similar[0].short, similar[0].long, - similar[0].argcount, similar[0].value) + o = Option( + similar[0].short, similar[0].long, similar[0].argcount, similar[0].value + ) if o.argcount == 0: if value is not None: - raise tokens.error('%s must not have an argument' % o.long) + raise tokens.error("%s must not have an argument" % o.long) else: if value is None: - if tokens.current() in [None, '--']: - raise tokens.error('%s requires argument' % o.long) + if tokens.current() in [None, "--"]: + raise tokens.error("%s requires argument" % o.long) value = tokens.move() if tokens.error is DocoptExit: o.value = value if value is not None else True @@ -334,32 +331,32 @@ def parse_long(tokens, options): def parse_shorts(tokens, options): """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" token = tokens.move() - assert token.startswith('-') and not token.startswith('--') - left = token.lstrip('-') + assert token.startswith("-") and not token.startswith("--") + left = token.lstrip("-") parsed = [] - while left != '': - short, left = '-' + left[0], left[1:] + while left != "": + short, left = "-" + left[0], left[1:] similar = [o for o in options if o.short == short] if len(similar) > 1: - raise tokens.error('%s is specified ambiguously %d times' % - (short, len(similar))) + raise tokens.error( + "%s is specified ambiguously %d times" % (short, len(similar)) + ) elif len(similar) < 1: o = Option(short, None, 0) options.append(o) if tokens.error is DocoptExit: o = Option(short, None, 0, True) else: # why copying is necessary here? - o = Option(short, similar[0].long, - similar[0].argcount, similar[0].value) + o = Option(short, similar[0].long, similar[0].argcount, similar[0].value) value = None if o.argcount != 0: - if left == '': - if tokens.current() in [None, '--']: - raise tokens.error('%s requires argument' % short) + if left == "": + if tokens.current() in [None, "--"]: + raise tokens.error("%s requires argument" % short) value = tokens.move() else: value = left - left = '' + left = "" if tokens.error is DocoptExit: o.value = value if value is not None else True parsed.append(o) @@ -370,17 +367,17 @@ def parse_pattern(source, options): tokens = Tokens.from_pattern(source) result = parse_expr(tokens, options) if tokens.current() is not None: - raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) + raise tokens.error("unexpected ending: %r" % " ".join(tokens)) return Required(*result) def parse_expr(tokens, options): """expr ::= seq ( '|' seq )* ;""" seq = parse_seq(tokens, options) - if tokens.current() != '|': + if tokens.current() != "|": return seq result = [Required(*seq)] if len(seq) > 1 else seq - while tokens.current() == '|': + while tokens.current() == "|": tokens.move() seq = parse_seq(tokens, options) result += [Required(*seq)] if len(seq) > 1 else seq @@ -390,9 +387,9 @@ def parse_expr(tokens, options): def parse_seq(tokens, options): """seq ::= ( atom [ '...' ] )* ;""" result = [] - while tokens.current() not in [None, ']', ')', '|']: + while tokens.current() not in [None, "]", ")", "|"]: atom = parse_atom(tokens, options) - if tokens.current() == '...': + if tokens.current() == "...": atom = [OneOrMore(*atom)] tokens.move() result += atom @@ -401,25 +398,25 @@ def parse_seq(tokens, options): def parse_atom(tokens, options): """atom ::= '(' expr ')' | '[' expr ']' | 'options' - | long | shorts | argument | command ; + | long | shorts | argument | command ; """ token = tokens.current() result = [] - if token in '([': + if token in "([": tokens.move() - matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] + matching, pattern = {"(": [")", Required], "[": ["]", Optional]}[token] result = pattern(*parse_expr(tokens, options)) if tokens.move() != matching: raise tokens.error("unmatched '%s'" % token) return [result] - elif token == 'options': + elif token == "options": tokens.move() return [OptionsShortcut()] - elif token.startswith('--') and token != '--': + elif token.startswith("--") and token != "--": return parse_long(tokens, options) - elif token.startswith('-') and token not in ('-', '--'): + elif token.startswith("-") and token not in ("-", "--"): return parse_shorts(tokens, options) - elif token.startswith('<') and token.endswith('>') or token.isupper(): + elif token.startswith("<") and token.endswith(">") or token.isupper(): return [Argument(tokens.move())] else: return [Command(tokens.move())] @@ -436,11 +433,11 @@ def parse_argv(tokens, options, options_first=False): """ parsed = [] while tokens.current() is not None: - if tokens.current() == '--': + if tokens.current() == "--": return parsed + [Argument(None, v) for v in tokens] - elif tokens.current().startswith('--'): + elif tokens.current().startswith("--"): parsed += parse_long(tokens, options) - elif tokens.current().startswith('-') and tokens.current() != '-': + elif tokens.current().startswith("-") and tokens.current() != "-": parsed += parse_shorts(tokens, options) elif options_first: return parsed + [Argument(None, v) for v in tokens] @@ -451,40 +448,42 @@ def parse_argv(tokens, options, options_first=False): def parse_defaults(doc): defaults = [] - for s in parse_section('options:', doc): + for s in parse_section("options:", doc): # FIXME corner case "bla: options: --foo" - _, _, s = s.partition(':') # get rid of "options:" - split = re.split('\n *(-\S+?)', '\n' + s)[1:] + _, _, s = s.partition(":") # get rid of "options:" + split = re.split("\n *(-\S+?)", "\n" + s)[1:] split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] - options = [Option.parse(s) for s in split if s.startswith('-')] + options = [Option.parse(s) for s in split if s.startswith("-")] defaults += options return defaults def parse_section(name, source): - pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', - re.IGNORECASE | re.MULTILINE) + pattern = re.compile( + "^([^\n]*" + name + "[^\n]*\n?(?:[ \t].*?(?:\n|$))*)", + re.IGNORECASE | re.MULTILINE, + ) return [s.strip() for s in pattern.findall(source)] def formal_usage(section): - _, _, section = section.partition(':') # drop "usage:" + _, _, section = section.partition(":") # drop "usage:" pu = section.split() - return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' + return "( " + " ".join(") | (" if s == pu[0] else s for s in pu[1:]) + " )" def extras(help, version, options, doc): - if help and any((o.name in ('-h', '--help')) and o.value for o in options): + if help and any((o.name in ("-h", "--help")) and o.value for o in options): print(doc.strip("\n")) sys.exit() - if version and any(o.name == '--version' and o.value for o in options): + if version and any(o.name == "--version" and o.value for o in options): print(version) sys.exit() class Dict(dict): def __repr__(self): - return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) + return "{%s}" % ",\n ".join("%r: %r" % i for i in sorted(self.items())) def docopt(doc, argv=None, help=True, version=None, options_first=False): @@ -552,7 +551,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False): """ argv = sys.argv[1:] if argv is None else argv - usage_sections = parse_section('usage:', doc) + usage_sections = parse_section("usage:", doc) if len(usage_sections) == 0: raise DocoptLanguageError('"usage:" (case-insensitive) not found.') if len(usage_sections) > 1: diff --git a/src/transform/cassius/cassius-import/bin/interactive.py b/src/transform/cassius/cassius-import/bin/interactive.py index a752b72ea4..0ef1dd0011 100755 --- a/src/transform/cassius/cassius-import/bin/interactive.py +++ b/src/transform/cassius/cassius-import/bin/interactive.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from __future__ import print_function + __author__ = "Martin Paul Eve" __email__ = "martin@martineve.com" @@ -29,20 +30,44 @@ class Interactive(Debuggable): def __init__(self, debug): self.debug = debug - Debuggable.__init__(self, 'Interactive Prompt Handler') + Debuggable.__init__(self, "Interactive Prompt Handler") # ANSI terminal colorization code heavily inspired by pygments: # http://dev.pocoo.org/hg/pygments-main/file/b2deea5b5030/pygments/console.py # (pygments is by Tim Hatch, Armin Ronacher, et al.) self.COLOR_ESCAPE = "\x1b[" - self.DARK_COLORS = ["black", "darkred", "darkgreen", "brown", "darkblue", - "purple", "teal", "lightgray"] - self.LIGHT_COLORS = ["darkgray", "red", "green", "yellow", "blue", - "fuchsia", "turquoise", "white"] + self.DARK_COLORS = [ + "black", + "darkred", + "darkgreen", + "brown", + "darkblue", + "purple", + "teal", + "lightgray", + ] + self.LIGHT_COLORS = [ + "darkgray", + "red", + "green", + "yellow", + "blue", + "fuchsia", + "turquoise", + "white", + ] self.RESET_COLOR = self.COLOR_ESCAPE + "39;49;00m" - def input_options(self, options, require=False, prompt=None, fallback_prompt=None, - numrange=None, default=None, max_width=72): + def input_options( + self, + options, + require=False, + prompt=None, + fallback_prompt=None, + numrange=None, + default=None, + max_width=72, + ): """Prompts a user for input. The sequence of `options` defines the choices the user has. A single-letter shortcut is inferred for each option; the user's choice is returned as that single, lower-case @@ -82,30 +107,31 @@ def input_options(self, options, require=False, prompt=None, fallback_prompt=Non found_letter = letter break else: - raise ValueError('no unambiguous lettering found') + raise ValueError("no unambiguous lettering found") letters[found_letter.lower()] = option index = option.index(found_letter) # Mark the option's shortcut letter for display. - if not require and ((default is None and not numrange and first) or - (isinstance(default, basestring) and - found_letter.lower() == default.lower())): + if not require and ( + (default is None and not numrange and first) + or ( + isinstance(default, basestring) + and found_letter.lower() == default.lower() + ) + ): # The first option is the default; mark it. - show_letter = '[%s]' % found_letter.upper() + show_letter = "[%s]" % found_letter.upper() is_default = True else: show_letter = found_letter.upper() is_default = False # Colorize the letter shortcut. - show_letter = self.colorize('green' if is_default else 'red', - show_letter) + show_letter = self.colorize("green" if is_default else "red", show_letter) # Insert the highlighted letter back into the word. - capitalized.append( - option[:index] + show_letter + option[index + 1:] - ) + capitalized.append(option[:index] + show_letter + option[index + 1 :]) display_letters.append(found_letter.upper()) first = False @@ -126,36 +152,35 @@ def input_options(self, options, require=False, prompt=None, fallback_prompt=Non if numrange: if isinstance(default, int): default_name = str(default) - default_name = self.colorize('turquoise', default_name) - tmpl = '# selection (default %s)' + default_name = self.colorize("turquoise", default_name) + tmpl = "# selection (default %s)" prompt_parts.append(tmpl % default_name) prompt_part_lengths.append(len(tmpl % str(default))) else: - prompt_parts.append('# selection') + prompt_parts.append("# selection") prompt_part_lengths.append(len(prompt_parts[-1])) prompt_parts += capitalized prompt_part_lengths += [len(s) for s in options] # Wrap the query text. - prompt = '' + prompt = "" line_length = 0 - for i, (part, length) in enumerate(zip(prompt_parts, - prompt_part_lengths)): + for i, (part, length) in enumerate(zip(prompt_parts, prompt_part_lengths)): # Add punctuation. if i == len(prompt_parts) - 1: - part += '?' + part += "?" else: - part += ',' + part += "," length += 1 # Choose either the current line or the beginning of the next. if line_length + length + 1 > max_width: - prompt += '\n' + prompt += "\n" line_length = 0 if line_length != 0: # Not the beginning of the line; need a space. - part = ' ' + part + part = " " + part length += 1 prompt += part @@ -164,10 +189,10 @@ def input_options(self, options, require=False, prompt=None, fallback_prompt=Non # Make a fallback prompt too. This is displayed if the user enters # something that is not recognized. if not fallback_prompt: - fallback_prompt = 'Enter one of ' + fallback_prompt = "Enter one of " if numrange: - fallback_prompt += '%i-%i, ' % numrange - fallback_prompt += ', '.join(display_letters) + ':' + fallback_prompt += "%i-%i, " % numrange + fallback_prompt += ", ".join(display_letters) + ":" resp = self.input_(prompt) while True: @@ -210,25 +235,25 @@ def input_(self, prompt=None): # http://bugs.python.org/issue1927 if prompt: if isinstance(prompt, unicode): - prompt = prompt.encode(self._encoding(), 'replace') - print(prompt, end=' ') + prompt = prompt.encode(self._encoding(), "replace") + print(prompt, end=" ") try: resp = raw_input() except EOFError: - self.debug.print_debug('stdin stream ended while input required') + self.debug.print_debug("stdin stream ended while input required") - return resp.decode(sys.stdin.encoding or 'utf8', 'ignore') + return resp.decode(sys.stdin.encoding or "utf8", "ignore") def _encoding(self): """Tries to guess the encoding used by the terminal.""" # Determine from locale settings. try: - return locale.getdefaultlocale()[1] or 'utf8' + return locale.getdefaultlocale()[1] or "utf8" except ValueError: # Invalid locale environment variable setting. To avoid # failing entirely for no good reason, assume UTF-8. - return 'utf8' + return "utf8" def _colorize(self, color, text): """Returns a string that prints the given text in the given color @@ -238,9 +263,11 @@ def _colorize(self, color, text): if color in self.DARK_COLORS: escape = self.COLOR_ESCAPE + "%im" % (self.DARK_COLORS.index(color) + 30) elif color in self.LIGHT_COLORS: - escape = self.COLOR_ESCAPE + "%i;01m" % (self.LIGHT_COLORS.index(color) + 30) + escape = self.COLOR_ESCAPE + "%i;01m" % ( + self.LIGHT_COLORS.index(color) + 30 + ) else: - raise ValueError('no such color %s', color) + raise ValueError("no such color %s", color) return escape + text + self.RESET_COLOR def colorize(self, color, text): @@ -249,7 +276,7 @@ def colorize(self, color, text): """ return self._colorize(color, text) - def _colordiff(self, a, b, highlight='red', minor_highlight='lightgray'): + def _colordiff(self, a, b, highlight="red", minor_highlight="lightgray"): """Given two values, return the same pair of strings except with their differences highlighted in the specified color. Strings are highlighted intelligently to show differences; other values are @@ -274,17 +301,17 @@ def _colordiff(self, a, b, highlight='red', minor_highlight='lightgray'): matcher = SequenceMatcher(lambda x: False, a, b) for op, a_start, a_end, b_start, b_end in matcher.get_opcodes(): - if op == 'equal': + if op == "equal": # In both strings. a_out.append(a[a_start:a_end]) b_out.append(b[b_start:b_end]) - elif op == 'insert': + elif op == "insert": # Right only. b_out.append(self.colorize(highlight, b[b_start:b_end])) - elif op == 'delete': + elif op == "delete": # Left only. a_out.append(self.colorize(highlight, a[a_start:a_end])) - elif op == 'replace': + elif op == "replace": # Right and left differ. Colorise with second highlight if # it's just a case change. if a[a_start:a_end].lower() != b[b_start:b_end].lower(): @@ -294,11 +321,11 @@ def _colordiff(self, a, b, highlight='red', minor_highlight='lightgray'): a_out.append(self.colorize(color, a[a_start:a_end])) b_out.append(self.colorize(color, b[b_start:b_end])) else: - assert(False) + assert False - return u''.join(a_out), u''.join(b_out) + return "".join(a_out), "".join(b_out) - def displayable_path(self, path, separator=u'; '): + def displayable_path(self, path, separator="; "): """Attempts to decode a bytestring path to a unicode object for the purpose of displaying it to the user. If the `path` argument is a list or a tuple, the elements are joined with `separator`. @@ -312,29 +339,29 @@ def displayable_path(self, path, separator=u'; '): return unicode(path) try: - return path.decode(self._fsencoding(), 'ignore') + return path.decode(self._fsencoding(), "ignore") except (UnicodeError, LookupError): - return path.decode('utf8', 'ignore') + return path.decode("utf8", "ignore") def _fsencoding(self): """Get the system's filesystem encoding. On Windows, this is always UTF-8 (not MBCS). """ encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() - if encoding == 'mbcs': + if encoding == "mbcs": # On Windows, a broken encoding known to Python as "MBCS" is # used for the filesystem. However, we only use the Unicode API # for Windows paths, so the encoding is actually immaterial so # we can avoid dealing with this nastiness. We arbitrarily # choose UTF-8. - encoding = 'utf8' + encoding = "utf8" return encoding - def colordiff(self, a, b, highlight='red'): + def colordiff(self, a, b, highlight="red"): """Colorize differences between two values if color is enabled. - (Like _colordiff but conditional.) - """ - if self.gv.settings.get_setting('color', self) == 'True': + (Like _colordiff but conditional.) + """ + if self.gv.settings.get_setting("color", self) == "True": return self._colordiff(a, b, highlight) else: return unicode(a), unicode(b) @@ -346,19 +373,19 @@ def print_(self, *strings): """ if strings: if isinstance(strings[0], unicode): - txt = u' '.join(strings) + txt = " ".join(strings) else: - txt = ' '.join(strings) + txt = " ".join(strings) else: - txt = u'' + txt = "" if isinstance(txt, unicode): - txt = txt.encode(self._encoding(), 'replace') + txt = txt.encode(self._encoding(), "replace") print(txt) - def color_diff_suffix(self, a, b, highlight='red'): + def color_diff_suffix(self, a, b, highlight="red"): """Colorize the differing suffix between two strings.""" a, b = unicode(a), unicode(b) - if not self.gv.settings.get_setting('color', self) == 'True': + if not self.gv.settings.get_setting("color", self) == "True": return a, b # Fast path. @@ -375,21 +402,21 @@ def color_diff_suffix(self, a, b, highlight='red'): first_diff = min(len(a), len(b)) # Colorize from the first difference on. - return a[:first_diff] + self.colorize(highlight, a[first_diff:]), \ - b[:first_diff] + self.colorize(highlight, b[first_diff:]) + return a[:first_diff] + self.colorize(highlight, a[first_diff:]), b[ + :first_diff + ] + self.colorize(highlight, b[first_diff:]) def choose_candidate(self, candidates, manipulate, opts, item=None, itemcount=None): - self.print_(u'Candidates:') + self.print_("Candidates:") for i, match in enumerate(candidates): - # Index, metadata, and distance. + # Index, metadata, and distance. line = [ - u'{0}.'.format(i + 1), - u'{0}'.format(manipulate.get_stripped_text(match.reference_to_link) - ) + "{0}.".format(i + 1), + "{0}".format(manipulate.get_stripped_text(match.reference_to_link)), ] - self.print_(' '.join(line)) + self.print_(" ".join(line)) # Ask the user for a choice. sel = self.input_options(opts, numrange=(1, len(candidates))) diff --git a/src/transform/epub.py b/src/transform/epub.py index 5ae1668a49..4db8f60571 100755 --- a/src/transform/epub.py +++ b/src/transform/epub.py @@ -17,16 +17,16 @@ def temp_directory(): - _dir = os.path.join(settings.BASE_DIR, 'files/temp', str(uuid.uuid4())) + _dir = os.path.join(settings.BASE_DIR, "files/temp", str(uuid.uuid4())) os.makedirs(_dir, 0o775) return _dir def manipulate_images(html): - souped_html = BeautifulSoup(str(html), 'html') + souped_html = BeautifulSoup(str(html), "html") elements = { - 'img': 'src', + "img": "src", } found_elements = [] @@ -38,39 +38,47 @@ def manipulate_images(html): # iterate over all found elements of each type in the elements dictionary for image in images: # attempt to pull a URL from the specified attribute - found_elements.append(os.path.basename(image['src'])) - image['src'] = os.path.basename(image['src']) + found_elements.append(os.path.basename(image["src"])) + image["src"] = os.path.basename(image["src"]) - css_tag = souped_html.new_tag("link", rel="stylesheet", type="text/css", href="style/default.css") + css_tag = souped_html.new_tag( + "link", rel="stylesheet", type="text/css", href="style/default.css" + ) souped_html.body.append(css_tag) return souped_html, found_elements def get_html_content(galley): - if galley.file.mime_type == 'application/xml': + if galley.file.mime_type == "application/xml": return galley.render() - elif galley.file.mime_type == 'text/html': + elif galley.file.mime_type == "text/html": return galley.file.get_file(galley.article) def write_html_to_temp(html, temp): - file = codecs.open(os.path.join(temp, 'temp.html'), "w", "utf-8") + file = codecs.open(os.path.join(temp, "temp.html"), "w", "utf-8") file.write(str(html)) file.close() def store_file(request, temp, file, galley): - new_file = files.copy_local_file_to_article(os.path.join(temp, file), 'article.epub', galley.article, - request.user, label="EPUB", galley=True) + new_file = files.copy_local_file_to_article( + os.path.join(temp, file), + "article.epub", + galley.article, + request.user, + label="EPUB", + galley=True, + ) galley.article.manuscript_files.add(new_file) models.Galley.objects.create( article=galley.article, file=new_file, - label='EPUB', - type='epub', - sequence=galley.article.get_next_galley_sequence() + label="EPUB", + type="epub", + sequence=galley.article.get_next_galley_sequence(), ) @@ -84,28 +92,34 @@ def generate_ebook_lib_epub(request, galley): book = epub.EpubBook() # add metadata - book.set_identifier('sample123456') + book.set_identifier("sample123456") book.set_title(article.title) - book.set_language('en') + book.set_language("en") for author in article.authors.all(): book.add_author(author.full_name()) - c1 = epub.EpubHtml(title=article.title, file_name='article.xhtml', lang='en') + c1 = epub.EpubHtml(title=article.title, file_name="article.xhtml", lang="en") c1.content = str(html) book.add_item(c1) for i, element in enumerate(elements): img = galley.images.get(original_filename=element) - item = epub.EpubItem(uid="image_{0}".format(i), - file_name=element, - content=open(img.self_article_path(), 'rb').read()) + item = epub.EpubItem( + uid="image_{0}".format(i), + file_name=element, + content=open(img.self_article_path(), "rb").read(), + ) book.add_item(item) - style = '''img {max-width: 100%;}''' - default_css = epub.EpubItem(uid="style_default", file_name="style/default.css", media_type="text/css", - content=style) + style = """img {max-width: 100%;}""" + default_css = epub.EpubItem( + uid="style_default", + file_name="style/default.css", + media_type="text/css", + content=style, + ) book.add_item(default_css) book.spine = [c1] diff --git a/src/transform/logic.py b/src/transform/logic.py index 86f998e43e..03e934c30c 100755 --- a/src/transform/logic.py +++ b/src/transform/logic.py @@ -13,17 +13,17 @@ class CassiusDriver: - """ A class to control the typesetting tool CaSSius and its JATS importer for automated XML typesetting - """ + """A class to control the typesetting tool CaSSius and its JATS importer for automated XML typesetting""" def __init__(self, accessible_temporary_directory, galley, request): - """ Creates a new CassiusDriver + """Creates a new CassiusDriver :param accessible_temporary_directory: a temporary directory to which the application has write permissions :param galley: a galley associated with an article and file :param request: the current request object """ from transform import urls + self.galley = galley self.accessible_temporary_directory = accessible_temporary_directory self.article_object = galley.article @@ -33,7 +33,7 @@ def __init__(self, accessible_temporary_directory, galley, request): self.rewritten_figures = {} def transform(self): - """ Transforms a JATS XML document into a PDF and affiliate it with the current article as a galley + """Transforms a JATS XML document into a PDF and affiliate it with the current article as a galley :return: None """ @@ -47,35 +47,39 @@ def transform(self): self._cleanup(uuid_directory_name) def import_from_jats(self): - """ Import a JATS file into CaSSius HTML + """Import a JATS file into CaSSius HTML :return: a 3-tuple of output_file_path, uuid_directory_name, uuid_file_name """ if not self._check_file_is_xml(): return None, None, None - uuid_directory_name, uuid_file_name = self._copy_files_to_accessible_temporary_directory() + uuid_directory_name, uuid_file_name = ( + self._copy_files_to_accessible_temporary_directory() + ) return self._transform_using_xsl(uuid_directory_name, uuid_file_name) def replace_logo(self, uuid_directory_name): - """ Replaces the CaSSius logo with the press logo + """Replaces the CaSSius logo with the press logo :param uuid_directory_name: the temporary directory on which we are working :return: None """ - javascript_path = os.path.join(uuid_directory_name, 'cassius', 'cassius.js') + javascript_path = os.path.join(uuid_directory_name, "cassius", "cassius.js") old_logo_text = "cassius/images/logo.png" - with open(javascript_path, 'r', encoding="utf-8") as javascript: + with open(javascript_path, "r", encoding="utf-8") as javascript: in_data = javascript.read() - out_data = in_data.replace(old_logo_text, self.request.press.press_cover(self.request)) + out_data = in_data.replace( + old_logo_text, self.request.press.press_cover(self.request) + ) - with open(javascript_path, 'w', encoding="utf-8") as new_javascript: + with open(javascript_path, "w", encoding="utf-8") as new_javascript: new_javascript.write(out_data) def replace_figures(self, uuid_directory_name, html): - """ Replace the file names of figures with the copied UUID versions. This will handle figures/file.jpg for + """Replace the file names of figures with the copied UUID versions. This will handle figures/file.jpg for example or file.jpg so long as there is a file affiliated with the article that has an original file name of file.jpg. @@ -83,18 +87,22 @@ def replace_figures(self, uuid_directory_name, html): :param html: the path to the CaSSius HTML :return: """ - with open(html, 'r', encoding="utf-8") as html_file_object: + with open(html, "r", encoding="utf-8") as html_file_object: in_data = html_file_object.read() for key, val in self.rewritten_figures.items(): - in_data = in_data.replace('figures/{0}'.format(key), os.path.join(uuid_directory_name, val)) - in_data = in_data.replace('{0}'.format(key), os.path.join(uuid_directory_name, val)) - - with open(html, 'w', encoding="utf-8") as new_html: + in_data = in_data.replace( + "figures/{0}".format(key), os.path.join(uuid_directory_name, val) + ) + in_data = in_data.replace( + "{0}".format(key), os.path.join(uuid_directory_name, val) + ) + + with open(html, "w", encoding="utf-8") as new_html: new_html.write(in_data) def print_to_pdf(self, html_file, uuid_directory_name, uuid_file_name): - """ Call pdfkit to create a PDF. + """Call pdfkit to create a PDF. :param html_file: the path to the HTML :param uuid_directory_name: the temporary directory on which we are working @@ -104,18 +112,20 @@ def print_to_pdf(self, html_file, uuid_directory_name, uuid_file_name): return self._call_pdfkit(html_file, uuid_directory_name, uuid_file_name) def _check_file_is_xml(self): - """ Checks for an acceptable XML input MIME type. + """Checks for an acceptable XML input MIME type. :return: True if XML, False otherwise """ - return self.file_object.mime_type == 'application/xml' + return self.file_object.mime_type == "application/xml" def _copy_files_to_accessible_temporary_directory(self): - """ Copies the XML, all images, and CaSSius templates to a temporary UUID directory + """Copies the XML, all images, and CaSSius templates to a temporary UUID directory :return: a 2-tuple of uuid_directory_name, uuid_file_name """ - uuid_directory_name = os.path.join(self.accessible_temporary_directory, str(uuid.uuid4())) + uuid_directory_name = os.path.join( + self.accessible_temporary_directory, str(uuid.uuid4()) + ) uuid_file_name = str(uuid.uuid4()) # create the sub-folders as necessary @@ -123,28 +133,38 @@ def _copy_files_to_accessible_temporary_directory(self): os.makedirs(uuid_directory_name, 0o777) # copy the XML file - shutil.copy(self.file_object.get_file_path(self.article_object), - os.path.join(uuid_directory_name, uuid_file_name)) + shutil.copy( + self.file_object.get_file_path(self.article_object), + os.path.join(uuid_directory_name, uuid_file_name), + ) # copy the CaSSius files - shutil.copytree(os.path.join(self.current_path, "cassius", "CaSSius", "cassius"), - os.path.join(uuid_directory_name, "cassius")) + shutil.copytree( + os.path.join(self.current_path, "cassius", "CaSSius", "cassius"), + os.path.join(uuid_directory_name, "cassius"), + ) # copy all data and figure files for data_file_object in self.article_object.data_figure_files.all(): new_data_file_name = str(uuid.uuid4()) - shutil.copy(data_file_object.get_file_path(self.article_object), - os.path.join(uuid_directory_name, new_data_file_name)) + shutil.copy( + data_file_object.get_file_path(self.article_object), + os.path.join(uuid_directory_name, new_data_file_name), + ) # store the old file name as a key and the new file name as a value in this dictionary - self.rewritten_figures[data_file_object.original_filename] = new_data_file_name + self.rewritten_figures[data_file_object.original_filename] = ( + new_data_file_name + ) for image in self.galley.images.all(): new_image_file_name = str(uuid.uuid4()) - shutil.copy(image.get_file_path(self.article_object), - os.path.join(uuid_directory_name, new_image_file_name)) + shutil.copy( + image.get_file_path(self.article_object), + os.path.join(uuid_directory_name, new_image_file_name), + ) # store the old file name as a key and the new file name as a value in this dictionary self.rewritten_figures[image.original_filename] = new_image_file_name @@ -152,25 +172,26 @@ def _copy_files_to_accessible_temporary_directory(self): return uuid_directory_name, uuid_file_name def _transform_using_xsl(self, uuid_directory_name, uuid_file_name): - """ Transforms a document from XML to HTML + """Transforms a document from XML to HTML :param uuid_directory_name: the temporary directory on which we are working :param uuid_file_name: the temporary file name of the XML :return: a 3-tuple of output_file_path, uuid_directory_name, uuid_file_name """ - xslt_path = os.path.join(self.current_path, 'cassius', 'cassius-import', 'bin') + xslt_path = os.path.join(self.current_path, "cassius", "cassius-import", "bin") xml_path = os.path.join(uuid_directory_name, uuid_file_name) - output_file_path = '{0}.html'.format(xml_path) + output_file_path = "{0}.html".format(xml_path) command = "java -cp '{0}{1}saxon9.jar':'{0}{1}..{1}runtime{1}xml-resolver-1.1.jar':'{0}{1}..{1}runtime{1}' net.sf.saxon.Transform -r:org.apache.xml.resolver.tools.CatalogResolver -y:org.apache.xml.resolver.tools.ResolvingXMLReader -x:org.apache.xml.resolver.tools.ResolvingXMLReader -u -o '{2}' '{3}' '{0}{1}..{1}transform{1}xsl{1}cassius-main.xsl'".format( - xslt_path, os.sep, output_file_path, xml_path) + xslt_path, os.sep, output_file_path, xml_path + ) subprocess.call(command, stdin=None, shell=True) return output_file_path, uuid_directory_name, uuid_file_name def _call_pdfkit(self, html_file, uuid_directory_name, uuid_file_name): - """ Runs wkhtmltopdf to create a PDF file. + """Runs wkhtmltopdf to create a PDF file. :param html_file: the input HTML :param uuid_directory_name: the temporary directory on which we are working @@ -178,47 +199,64 @@ def _call_pdfkit(self, html_file, uuid_directory_name, uuid_file_name): :return: the output file path """ pdfkit_options = { - 'margin-top': '0', - 'margin-right': '0', - 'margin-bottom': '0', - 'margin-left': '0', - 'encoding': 'UTF-8', - 'javascript-delay': '9000', - 'no-stop-slow-scripts': '', + "margin-top": "0", + "margin-right": "0", + "margin-bottom": "0", + "margin-left": "0", + "encoding": "UTF-8", + "javascript-delay": "9000", + "no-stop-slow-scripts": "", } pdfkit_config = pdfkit.configuration( - wkhtmltopdf=bytes(os.path.join(self.current_path, 'cassius/' 'bin', 'wkhtmltopdf'), 'utf-8') + wkhtmltopdf=bytes( + os.path.join(self.current_path, "cassius/" "bin", "wkhtmltopdf"), + "utf-8", + ) ) - pdfkit_output_file = '{0}.pdf'.format(os.path.join(uuid_directory_name, uuid_file_name)) + pdfkit_output_file = "{0}.pdf".format( + os.path.join(uuid_directory_name, uuid_file_name) + ) - pdfkit.from_file(html_file, pdfkit_output_file, options=pdfkit_options, configuration=pdfkit_config) + pdfkit.from_file( + html_file, + pdfkit_output_file, + options=pdfkit_options, + configuration=pdfkit_config, + ) return pdfkit_output_file def _store_file(self, file_path): - """ Affiliates a file with an article + """Affiliates a file with an article :param file_path: the file path :return: None """ from core import models as core_models - new_file = files.copy_local_file_to_article(file_path, 'article.pdf', self.article_object, - self.request.user, label="PDF", galley=True) + + new_file = files.copy_local_file_to_article( + file_path, + "article.pdf", + self.article_object, + self.request.user, + label="PDF", + galley=True, + ) self.article_object.manuscript_files.add(new_file) core_models.Galley.objects.create( article=self.article_object, file=new_file, - label='PDF', - type='pdf', - sequence=self.article_object.get_next_galley_sequence() + label="PDF", + type="pdf", + sequence=self.article_object.get_next_galley_sequence(), ) @staticmethod def _cleanup(uuid_directory_name): - """ Deletes a temporary directory + """Deletes a temporary directory :param uuid_directory_name: the temporary directory to delete :return: None diff --git a/src/transform/urls.py b/src/transform/urls.py index 67f853df64..69cc3b91ec 100755 --- a/src/transform/urls.py +++ b/src/transform/urls.py @@ -7,9 +7,14 @@ from transform import views urlpatterns = [ - re_path(r'^galley/(?P\d+?)/generate-pdf/$', views.cassius_generate, - name='cassius_generate'), - - re_path(r'^galley/(?P\d+?)/generate-epub/$', views.epub_generate, - name='epub_generate'), + re_path( + r"^galley/(?P\d+?)/generate-pdf/$", + views.cassius_generate, + name="cassius_generate", + ), + re_path( + r"^galley/(?P\d+?)/generate-epub/$", + views.epub_generate, + name="epub_generate", + ), ] diff --git a/src/transform/views.py b/src/transform/views.py index 4c25e62f77..ada415bd37 100755 --- a/src/transform/views.py +++ b/src/transform/views.py @@ -11,7 +11,10 @@ from django.shortcuts import get_object_or_404, redirect from core import models as core_models -from security.decorators import typesetting_user_or_production_user_or_editor_required, has_request +from security.decorators import ( + typesetting_user_or_production_user_or_editor_required, + has_request, +) from transform.logic import CassiusDriver from transform import epub @@ -21,23 +24,21 @@ @has_request @typesetting_user_or_production_user_or_editor_required def cassius_generate(request, galley_id): - galley = get_object_or_404(core_models.Galley, pk=galley_id) - temporary_directory = os.path.join(settings.BASE_DIR, 'files', 'temp') + temporary_directory = os.path.join(settings.BASE_DIR, "files", "temp") driver = CassiusDriver(temporary_directory, galley, request) driver.transform() - return redirect(request.GET['return']) + return redirect(request.GET["return"]) @login_required @has_request @typesetting_user_or_production_user_or_editor_required def epub_generate(request, galley_id): - galley = get_object_or_404(core_models.Galley, pk=galley_id) epub.generate_ebook_lib_epub(request, galley) - return redirect(request.GET['return']) + return redirect(request.GET["return"]) diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 8dc7e5c752..b086936821 100755 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -20,33 +20,32 @@ MERGEABLE_SETTINGS = {"INSTALLED_APPS", "MIDDLEWARE"} -def load_janeway_settings(): +def load_janeway_settings(): with LOCK: if settings.configured: return settings_module = os.environ["JANEWAY_SETTINGS_MODULE"] janeway_settings = { - k: v for k, v in janeway_global_settings.__dict__.items() - if k.isupper() + k: v for k, v in janeway_global_settings.__dict__.items() if k.isupper() } if os.environ["JANEWAY_SETTINGS_MODULE"] != "core.janeway_global_settings": - custom_module = importlib.import_module(os.environ["JANEWAY_SETTINGS_MODULE"]) + custom_module = importlib.import_module( + os.environ["JANEWAY_SETTINGS_MODULE"] + ) custom_settings = { - k: v for k, v in custom_module.__dict__.items() - if k.isupper() + k: v for k, v in custom_module.__dict__.items() if k.isupper() } logging.info( - "Loading settings from %s" % ( - os.environ["JANEWAY_SETTINGS_MODULE"], - )) + "Loading settings from %s" % (os.environ["JANEWAY_SETTINGS_MODULE"],) + ) logging.debug( - "Loading the following custom settings: %s" %( - custom_settings.keys(), - )) + "Loading the following custom settings: %s" % (custom_settings.keys(),) + ) mergeable_settings = custom_settings.get( - "MERGEABLE_SETTINGS", MERGEABLE_SETTINGS, + "MERGEABLE_SETTINGS", + MERGEABLE_SETTINGS, ) for k, v in custom_settings.items(): if k in mergeable_settings: @@ -57,10 +56,12 @@ def load_janeway_settings(): settings.configure(**janeway_settings) django.setup() + @singledispatch def merge_settings(base, override): return override + @merge_settings.register(list) @merge_settings.register(tuple) @merge_settings.register(set) @@ -68,6 +69,7 @@ def merge_iterable_settings(base, override): factory = type(base) return factory(itertools.chain(base, override)) + @merge_settings.register(dict) def merge_dict_settings(base, override): merged = {} @@ -82,18 +84,19 @@ def merge_dict_settings(base, override): def janeway_test_runner_wrapper(*args, **kwargs): - """ A test runner wrapper that will initialise the test database + """A test runner wrapper that will initialise the test database The original test runner will still be returned, but ensuring the required state exists in the database before the test runs """ - if not getattr(settings, 'CHOSEN_TEST_RUNNER', None): - settings.CHOSEN_TEST_RUNNER = 'django.test.runner.DiscoverRunner' + if not getattr(settings, "CHOSEN_TEST_RUNNER", None): + settings.CHOSEN_TEST_RUNNER = "django.test.runner.DiscoverRunner" from utils import install + install.update_settings(management_command=False) install.update_emails(management_command=False) install.update_xsl_files(management_command=False) settings.TEST_RUNNER = settings.CHOSEN_TEST_RUNNER - TestRunner = get_runner(settings) + TestRunner = get_runner(settings) return TestRunner(*args, **kwargs) diff --git a/src/utils/admin.py b/src/utils/admin.py index 4c3f682485..f9a8840d1b 100755 --- a/src/utils/admin.py +++ b/src/utils/admin.py @@ -10,32 +10,65 @@ class ImportCacheAdmin(admin.ModelAdmin): - list_display = ('url', 'mime_type', 'date_time') - list_filter = ('mime_type',) - search_fields = ('url', 'on_disk') - date_hierarchy = ('date_time') + list_display = ("url", "mime_type", "date_time") + list_filter = ("mime_type",) + search_fields = ("url", "on_disk") + date_hierarchy = "date_time" class PluginAdmin(admin.ModelAdmin): - list_display = ('name', 'display_name', 'version', 'date_installed', - 'enabled', 'press_wide') - list_filter = ('name', 'display_name', 'version', 'date_installed', - 'enabled', 'press_wide') - search_fields = ('name', 'display_name') - date_hierarchy = ('date_installed') + list_display = ( + "name", + "display_name", + "version", + "date_installed", + "enabled", + "press_wide", + ) + list_filter = ( + "name", + "display_name", + "version", + "date_installed", + "enabled", + "press_wide", + ) + search_fields = ("name", "display_name") + date_hierarchy = "date_installed" class LogAdmin(admin.ModelAdmin): - list_display = ('pk', 'types', 'date', 'level', 'actor', '_to', - 'is_email', 'email_subject', 'target') - list_filter = (admin_utils.GenericRelationArticleJournalFilter, - admin_utils.GenericRelationPreprintRepositoryFilter, - 'date', 'is_email', 'types', 'addressee__field') - search_fields = ('types', 'email_subject', 'actor__email', - 'actor__first_name', 'actor__last_name', - 'ip_address', 'email_subject', 'addressee__email') - date_hierarchy = ('date') - raw_id_fields = ('actor',) + list_display = ( + "pk", + "types", + "date", + "level", + "actor", + "_to", + "is_email", + "email_subject", + "target", + ) + list_filter = ( + admin_utils.GenericRelationArticleJournalFilter, + admin_utils.GenericRelationPreprintRepositoryFilter, + "date", + "is_email", + "types", + "addressee__field", + ) + search_fields = ( + "types", + "email_subject", + "actor__email", + "actor__first_name", + "actor__last_name", + "ip_address", + "email_subject", + "addressee__email", + ) + date_hierarchy = "date" + raw_id_fields = ("actor",) inlines = [ admin_utils.AddresseeInline, @@ -47,17 +80,17 @@ def _to(self, obj): class VersionAdmin(admin.ModelAdmin): - list_display = ('number', 'date', 'rollback') - list_filter = ('number', 'date', 'rollback') - search_fields = ('number',) - date_hierarchy = ('date') + list_display = ("number", "date", "rollback") + list_filter = ("number", "date", "rollback") + search_fields = ("number",) + date_hierarchy = "date" admin_list = [ (models.LogEntry, LogAdmin), (models.Plugin, PluginAdmin), (models.ImportCacheEntry, ImportCacheAdmin), - (models.Version, VersionAdmin) + (models.Version, VersionAdmin), ] [admin.site.register(*t) for t in admin_list] diff --git a/src/utils/admin_utils.py b/src/utils/admin_utils.py index e7541d5416..451f5bbb18 100644 --- a/src/utils/admin_utils.py +++ b/src/utils/admin_utils.py @@ -23,9 +23,7 @@ class JanewayModelAdmin(admin.ModelAdmin): formfield_overrides = { - DateTimePickerModelField: { - "widget": DateTimePickerInput - }, + DateTimePickerModelField: {"widget": DateTimePickerInput}, } @@ -39,13 +37,13 @@ class ArticleFKModelAdmin(JanewayModelAdmin): """ def _journal(self, obj): - return obj.article.journal if obj and obj.article else '' + return obj.article.journal if obj and obj.article else "" def _article(self, obj): - return truncatewords_html(str(obj.article), 5) if obj else '' + return truncatewords_html(str(obj.article), 5) if obj else "" def _author(self, obj): - return obj.article.correspondence_author if obj else '' + return obj.article.correspondence_author if obj else "" class PreprintFKModelAdmin(JanewayModelAdmin): @@ -58,24 +56,24 @@ class PreprintFKModelAdmin(JanewayModelAdmin): """ def _repository(self, obj): - return obj.preprint.repository if obj else '' + return obj.preprint.repository if obj else "" def _preprint(self, obj): - return truncatewords_html(str(obj.preprint), 5) if obj else '' + return truncatewords_html(str(obj.preprint), 5) if obj else "" class AccountInterestInline(admin.TabularInline): model = core_models.Account.interest.through extra = 0 - raw_id_fields = ('account',) + raw_id_fields = ("account",) class PostInline(admin.TabularInline): model = discussion_models.Post extra = 0 - raw_id_fields = ('owner', 'thread') - exclude = ('read_by',) - ordering = ('posted',) + raw_id_fields = ("owner", "thread") + exclude = ("read_by",) + ordering = ("posted",) class ReviewAssignmentAnswerInline(admin.TabularInline): @@ -86,7 +84,7 @@ class ReviewAssignmentAnswerInline(admin.TabularInline): class RevisionActionInline(admin.TabularInline): model = review_models.RevisionRequest.actions.through extra = 0 - raw_id_fields = ('revisionaction',) + raw_id_fields = ("revisionaction",) class AddresseeInline(admin.TabularInline): @@ -97,57 +95,57 @@ class AddresseeInline(admin.TabularInline): class IdentifierCrossrefStatusInline(admin.TabularInline): model = identifier_models.CrossrefStatus extra = 0 - filter_horizontal = ('deposits',) + filter_horizontal = ("deposits",) class DepositCrossrefStatusInline(admin.TabularInline): model = identifier_models.CrossrefStatus.deposits.through extra = 0 - raw_id_fields = ('crossrefstatus',) + raw_id_fields = ("crossrefstatus",) class AuthorReviewInline(admin.TabularInline): model = copyediting_models.AuthorReview - exclude = ('files_updated',) + exclude = ("files_updated",) extra = 0 class ArticleInline(admin.TabularInline): model = submission_models.Article extra = 0 - fields = ('title', 'correspondence_author', 'journal') - readonly_fields = ('title', 'correspondence_author', 'journal') - fk_name = 'primary_issue' + fields = ("title", "correspondence_author", "journal") + readonly_fields = ("title", "correspondence_author", "journal") + fk_name = "primary_issue" class ArticleOrderingInline(admin.TabularInline): model = journal_models.ArticleOrdering extra = 0 - raw_id_fields = ('article', 'issue', 'section') + raw_id_fields = ("article", "issue", "section") class CronTaskInline(admin.TabularInline): model = cron_models.CronTask extra = 0 - raw_id_fields = ('article',) + raw_id_fields = ("article",) class IdentifierInline(admin.TabularInline): model = identifier_models.Identifier extra = 0 - raw_id_fields = ('article',) + raw_id_fields = ("article",) class NoteInline(admin.TabularInline): model = submission_models.Note extra = 0 - raw_id_fields = ('article', 'creator') + raw_id_fields = ("article", "creator") class FieldAnswerInline(admin.TabularInline): model = submission_models.FieldAnswer extra = 0 - raw_id_fields = ('article',) + raw_id_fields = ("article",) class ArticleStageLogInline(admin.TabularInline): @@ -158,27 +156,26 @@ class ArticleStageLogInline(admin.TabularInline): class KeywordArticleInline(admin.TabularInline): model = submission_models.KeywordArticle extra = 0 - raw_id_fields = ('article', 'keyword') + raw_id_fields = ("article", "keyword") class GalleyInline(admin.TabularInline): model = core_models.Galley extra = 0 - fields = ('file', 'public', 'label', 'type', 'sequence') - raw_id_fields = ('article', 'file', 'css_file', - 'images', 'xsl_file') + fields = ("file", "public", "label", "type", "sequence") + raw_id_fields = ("article", "file", "css_file", "images", "xsl_file") class IssueGalleyInline(admin.TabularInline): model = journal_models.IssueGalley extra = 0 - raw_id_fields = ('file',) + raw_id_fields = ("file",) class SectionOrderingInline(admin.TabularInline): model = journal_models.SectionOrdering extra = 0 - raw_id_fields = ('section', 'issue') + raw_id_fields = ("section", "issue") class PasswordResetInline(admin.TabularInline): @@ -199,32 +196,35 @@ class SettingInline(admin.TabularInline): class SettingValueInline(admin.TabularInline): model = core_models.SettingValue extra = 0 - fields = ('journal', 'value') + fields = ("journal", "value") class FileInline(admin.TabularInline): model = core_models.File extra = 0 - fields = ('journal', 'value') + fields = ("journal", "value") class EditorialGroupMemberInline(admin.TabularInline): model = core_models.EditorialGroupMember extra = 0 - raw_id_fields = ('user',) + raw_id_fields = ("user",) class StaffGroupMemberInline(admin.TabularInline): model = press_models.StaffGroupMember extra = 0 - exclude = ('alternate_title', 'publications') - raw_id_fields = ('user',) + exclude = ("alternate_title", "publications") + raw_id_fields = ("user",) class WorkflowLogInline(admin.TabularInline): model = core_models.WorkflowLog extra = 0 - raw_id_fields = ('element', 'article',) + raw_id_fields = ( + "element", + "article", + ) class RepositoryRoleInline(admin.TabularInline): @@ -235,66 +235,75 @@ class RepositoryRoleInline(admin.TabularInline): class CommentInline(admin.TabularInline): model = repository_models.Comment extra = 0 - raw_id_fields = ('reply_to', 'preprint', 'author') - ordering = ('date_time',) + raw_id_fields = ("reply_to", "preprint", "author") + ordering = ("date_time",) class RepositoryFieldAnswerInline(admin.TabularInline): model = repository_models.RepositoryFieldAnswer extra = 0 - raw_id_fields = ('field', 'preprint',) + raw_id_fields = ( + "field", + "preprint", + ) class PreprintVersionInline(admin.TabularInline): model = repository_models.PreprintVersion extra = 0 - raw_id_fields = ('preprint', 'file', 'moderated_version') - exclude = ('abstract', 'published_doi') + raw_id_fields = ("preprint", "file", "moderated_version") + exclude = ("abstract", "published_doi") class KeywordPreprintInline(admin.TabularInline): model = repository_models.KeywordPreprint extra = 0 - raw_id_fields = ('preprint', 'keyword') + raw_id_fields = ("preprint", "keyword") class PreprintFileInline(admin.TabularInline): model = repository_models.PreprintFile extra = 0 - raw_id_fields = ('preprint',) + raw_id_fields = ("preprint",) class PreprintSupplementaryFileInline(admin.TabularInline): model = repository_models.PreprintSupplementaryFile extra = 0 - raw_id_fields = ('preprint',) + raw_id_fields = ("preprint",) class PreprintAuthorInline(admin.TabularInline): model = repository_models.PreprintAuthor extra = 0 - raw_id_fields = ('preprint', 'account') + raw_id_fields = ("preprint", "account") class VersionQueueInline(admin.TabularInline): model = repository_models.VersionQueue extra = 0 - raw_id_fields = ('preprint', 'file') - exclude = ('abstract', 'published_doi') + raw_id_fields = ("preprint", "file") + exclude = ("abstract", "published_doi") class RepositoryReviewInline(admin.TabularInline): model = repository_models.Review extra = 0 - raw_id_fields = ('preprint', 'manager', 'reviewer') - fields = ('manager', 'reviewer', 'date_due', - 'date_accepted', 'date_completed', 'status') + raw_id_fields = ("preprint", "manager", "reviewer") + fields = ( + "manager", + "reviewer", + "date_due", + "date_accepted", + "date_completed", + "status", + ) class NewsItemInline(admin.TabularInline): model = comms_models.NewsItem.tags.through extra = 0 - raw_id_fields = ('newsitem',) + raw_id_fields = ("newsitem",) class JournalFilterBase(admin.SimpleListFilter): @@ -302,13 +311,12 @@ class JournalFilterBase(admin.SimpleListFilter): A base class for other journal filters """ - title = 'journal' - parameter_name = 'journal' + title = "journal" + parameter_name = "journal" def lookups(self, request, model_admin): return ( - (journal.id, journal) - for journal in journal_models.Journal.objects.all() + (journal.id, journal) for journal in journal_models.Journal.objects.all() ) @@ -317,8 +325,9 @@ class ArticleIDJournalFilter(JournalFilterBase): A journal filter for objects that just store article ids, like core.File. """ + def queryset(self, request, queryset): - journal_pk = request.GET.get('journal', None) + journal_pk = request.GET.get("journal", None) if not journal_pk: return queryset articles = submission_models.Article.objects.filter( @@ -332,8 +341,9 @@ class FileArticleIDJournalFilter(JournalFilterBase): A journal filter for objects related to files, like core.SupplementaryFile. """ + def queryset(self, request, queryset): - journal_pk = request.GET.get('journal', None) + journal_pk = request.GET.get("journal", None) if not journal_pk: return queryset articles = submission_models.Article.objects.filter( @@ -346,15 +356,16 @@ class SentReminderJournalFilter(JournalFilterBase): """ A journal filter for SentReminder, which stores object IDs and types. """ + def queryset(self, request, queryset): - journal_pk = request.GET.get('journal', None) + journal_pk = request.GET.get("journal", None) if not journal_pk or not queryset: return queryset - if queryset.first().type == 'review': + if queryset.first().type == "review": model = review_models.ReviewAssignment - elif queryset.first().type == 'accepted-review': + elif queryset.first().type == "accepted-review": model = review_models.ReviewAssignment - elif queryset.first().type == 'revisions': + elif queryset.first().type == "revisions": model = review_models.RevisionRequest else: return queryset @@ -371,8 +382,9 @@ class GenericRelationJournalFilter(JournalFilterBase): the journal by a Generic Foreign Key. An example is cms.NavigationItem. """ + def queryset(self, request, queryset): - journal_pk = request.GET.get('journal', None) + journal_pk = request.GET.get("journal", None) if not journal_pk: return queryset journal = journal_models.Journal.objects.get(id=journal_pk) @@ -390,17 +402,14 @@ class GenericRelationPressFilter(admin.SimpleListFilter): An example is cms.NavigationItem. """ - title = 'press' - parameter_name = 'press' + title = "press" + parameter_name = "press" def lookups(self, request, model_admin): - return ( - (press.id, press.name) - for press in press_models.Press.objects.all() - ) + return ((press.id, press.name) for press in press_models.Press.objects.all()) def queryset(self, request, queryset): - press_pk = request.GET.get('press', None) + press_pk = request.GET.get("press", None) if not press_pk: return queryset press = press_models.Press.objects.get(id=press_pk) @@ -419,12 +428,10 @@ class GenericRelationArticleJournalFilter(JournalFilterBase): """ def queryset(self, request, queryset): - journal_pk = request.GET.get('journal', None) + journal_pk = request.GET.get("journal", None) if not journal_pk: return queryset - articles = submission_models.Article.objects.filter( - journal__id=journal_pk - ) + articles = submission_models.Article.objects.filter(journal__id=journal_pk) if not articles: return queryset.none() content_type = ContentType.objects.get_for_model(articles.first()) @@ -441,8 +448,8 @@ class GenericRelationPreprintRepositoryFilter(admin.SimpleListFilter): An example is utils.LogEntry. """ - title = 'repository' - parameter_name = 'repository' + title = "repository" + parameter_name = "repository" def lookups(self, request, model_admin): return ( @@ -451,12 +458,10 @@ def lookups(self, request, model_admin): ) def queryset(self, request, queryset): - repo_pk = request.GET.get('repository', None) + repo_pk = request.GET.get("repository", None) if not repo_pk: return queryset - preprints = repository_models.Preprint.objects.filter( - repository__id=repo_pk - ) + preprints = repository_models.Preprint.objects.filter(repository__id=repo_pk) if not preprints: return queryset.none() content_type = ContentType.objects.get_for_model(preprints.first()) diff --git a/src/utils/apps.py b/src/utils/apps.py index 6942bfdb5c..b48c40fa60 100755 --- a/src/utils/apps.py +++ b/src/utils/apps.py @@ -7,4 +7,4 @@ class UtilsConfig(AppConfig): - name = 'utils' + name = "utils" diff --git a/src/utils/const.py b/src/utils/const.py index d8021b943d..3bacb6ac0f 100644 --- a/src/utils/const.py +++ b/src/utils/const.py @@ -1,11 +1,11 @@ from enum import Enum, EnumMeta from bleach_allowlist import ( all_styles as _all_styles, - generally_xss_safe as _generally_xss_safe + generally_xss_safe as _generally_xss_safe, ) -class EnumContainsMeta(EnumMeta): +class EnumContainsMeta(EnumMeta): def __contains__(cls, value): try: cls(value) @@ -13,6 +13,7 @@ def __contains__(cls, value): return False return True + class EnumContains(Enum, metaclass=EnumContainsMeta): pass @@ -20,11 +21,14 @@ class EnumContains(Enum, metaclass=EnumContainsMeta): def get_allowed_html_tags(): return _generally_xss_safe + def get_allowed_html_tags_minimal(): - return ['span', 'em', 'i', 'b', 'strong', 'sup', 'sub'] + return ["span", "em", "i", "b", "strong", "sup", "sub"] + def get_allowed_attributes_minimal(): - return ['lang'] + return ["lang"] + def get_allowed_css_styles(): return _all_styles diff --git a/src/utils/db_functions.py b/src/utils/db_functions.py index b7ecddd2eb..d52b90721d 100644 --- a/src/utils/db_functions.py +++ b/src/utils/db_functions.py @@ -29,24 +29,24 @@ class GroupConcat(Aggregate): output_field = TextField() - def __init__(self, expression, separator=', ', distinct=False, **extra): + def __init__(self, expression, separator=", ", distinct=False, **extra): super().__init__(expression, distinct=distinct, **extra) - self.extra['separator'] = separator + self.extra["separator"] = separator self.vendor = connection.vendor - if self.vendor == 'postgresql': - self.function = 'STRING_AGG' + if self.vendor == "postgresql": + self.function = "STRING_AGG" self.template = "%(function)s(%(distinct)s%(expressions)s, '%(separator)s')" - elif self.vendor == 'mysql': - self.function = 'GROUP_CONCAT' - self.template = "%(function)s(%(distinct)s%(expressions)s SEPARATOR '%(separator)s')" - elif self.vendor == 'sqlite': - self.function = 'GROUP_CONCAT' + elif self.vendor == "mysql": + self.function = "GROUP_CONCAT" + self.template = ( + "%(function)s(%(distinct)s%(expressions)s SEPARATOR '%(separator)s')" + ) + elif self.vendor == "sqlite": + self.function = "GROUP_CONCAT" self.template = "%(function)s(%(expressions)s, '%(separator)s')" - elif self.vendor == 'mssql': - self.function = 'STRING_AGG' + elif self.vendor == "mssql": + self.function = "STRING_AGG" self.template = "%(function)s(%(expressions)s, '%(separator)s')" else: - raise NotImplementedError( - f"{self.vendor} is not supported for GroupConcat" - ) + raise NotImplementedError(f"{self.vendor} is not supported for GroupConcat") diff --git a/src/utils/decorators.py b/src/utils/decorators.py index 0bdb38ce32..0edfd89a13 100644 --- a/src/utils/decorators.py +++ b/src/utils/decorators.py @@ -1,6 +1,7 @@ """ Utility decorators """ + __copyright__ = "Copyright 2020 Birkbeck, University of London" __author__ = "Birkbeck Centre for Technology and Publishing" __license__ = "AGPL v3" @@ -18,14 +19,16 @@ def retry(attempts=3, throttle=0, exc=Exception): - """ Retries calling a function when certain exceptions are raised + """Retries calling a function when certain exceptions are raised :param attempts: maximum number of times to retry :param throttle_ms: optional throttling between calls in seconds :param exc: An exception (or tuple of exceptions) on which to retry """ + def wrapper(f): f_name = f.__name__ msg = "%s failed with %s: %s, Retrying in %d s..." + @wraps(f) def decorator(*args, **kwargs): tries = 1 @@ -38,10 +41,11 @@ def decorator(*args, **kwargs): logger.debug("Sleeping thread for %s" % throttle) time.sleep(throttle) finally: - tries +=1 + tries += 1 return f(*args, **kwargs) return decorator + return wrapper @@ -51,12 +55,10 @@ def GET_language_override(func): templates/admin/elements/translations/form_tabs.html Usage: adding ?language=code eg language=es will override the language for the view. """ + def language_override_wrapper(request, *args, **kwargs): - language = request.GET.get('language', None) + language = request.GET.get("language", None) request.override_language = language if language else translation.get_language() return func(request, *args, **kwargs) return language_override_wrapper - - - diff --git a/src/utils/forms.py b/src/utils/forms.py index 85c1a128cc..394f462a2d 100644 --- a/src/utils/forms.py +++ b/src/utils/forms.py @@ -31,7 +31,7 @@ def __init__(self, *args, **kwargs): class FakeModelForm(ModelForm): - """ A form that can't be saved + """A form that can't be saved Usefull for rendering a sample form """ @@ -55,20 +55,19 @@ def clean(self, *args, **kwargs): class KeywordModelForm(ModelForm): - """ A ModelForm for models implementing a Keyword M2M relationship """ - keywords = CharField( - required=False, help_text=_("Hit Enter to add a new keyword.")) + """A ModelForm for models implementing a Keyword M2M relationship""" + + keywords = CharField(required=False, help_text=_("Hit Enter to add a new keyword.")) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance.pk: - current_keywords = self.instance.keywords.values_list( - "word", flat=True) + current_keywords = self.instance.keywords.values_list("word", flat=True) field = self.fields["keywords"] field.initial = ",".join(current_keywords) def save(self, commit=True, *args, **kwargs): - posted_keywords = self.cleaned_data.get( 'keywords', '') + posted_keywords = self.cleaned_data.get("keywords", "") instance = super().save(commit=commit, *args, **kwargs) instance.keywords.clear() @@ -76,8 +75,7 @@ def save(self, commit=True, *args, **kwargs): if posted_keywords: keyword_list = posted_keywords.split(",") for i, keyword in enumerate(keyword_list): - obj, _ = submission_models.Keyword.objects.get_or_create( - word=keyword) + obj, _ = submission_models.Keyword.objects.get_or_create(word=keyword) instance.keywords.add(obj) if commit: instance.save() @@ -85,7 +83,7 @@ def save(self, commit=True, *args, **kwargs): class HTMLDateInput(DateInput): - input_type = 'date' + input_type = "date" def __init__(self, **kwargs): kwargs["format"] = "%Y-%m-%d" @@ -93,7 +91,7 @@ def __init__(self, **kwargs): class HTMLSwitchInput(CheckboxInput): - template_name = 'admin/elements/forms/foundation_switch_input.html' + template_name = "admin/elements/forms/foundation_switch_input.html" class CaptchaForm(Form): @@ -103,12 +101,12 @@ def __init__(self, *args, **kwargs): # Used by simple math captcha self.question_template = None - if settings.CAPTCHA_TYPE == 'simple_math': - self.question_template = _('What is %(num1)i %(operator)s %(num2)i? ') - captcha = MathCaptchaField(label=_('Answer this question: ')) - elif settings.CAPTCHA_TYPE == 'recaptcha': + if settings.CAPTCHA_TYPE == "simple_math": + self.question_template = _("What is %(num1)i %(operator)s %(num2)i? ") + captcha = MathCaptchaField(label=_("Answer this question: ")) + elif settings.CAPTCHA_TYPE == "recaptcha": captcha = ReCaptchaField(widget=ReCaptchaWidget()) - elif settings.CAPTCHA_TYPE == 'hcaptcha': + elif settings.CAPTCHA_TYPE == "hcaptcha": captcha = hCaptchaField() else: captcha = CharField(widget=HiddenInput, required=False) @@ -117,7 +115,7 @@ def __init__(self, *args, **kwargs): def text_sanitizer(text_value, tags=None, attrs=None, excl=ENTITIES_MAP): - """ A sanitizer for clearing potential harmful html/css/js from the input + """A sanitizer for clearing potential harmful html/css/js from the input :param text_value: the string to sanitize :param tags: A list of allowed html tags :param attrs: A dict of allowed html attributes @@ -143,12 +141,10 @@ def text_sanitizer(text_value, tags=None, attrs=None, excl=ENTITIES_MAP): def plain_text_validator(value): - """ A field validator that ensures a textual input has no harmful code""" + """A field validator that ensures a textual input has no harmful code""" string_with_no_carriage_returns = value.replace("\r", "") sanitized = text_sanitizer(string_with_no_carriage_returns) if string_with_no_carriage_returns != sanitized: - raise ValidationError( - _("HTML is not allowed in this field") - ) + raise ValidationError(_("HTML is not allowed in this field")) diff --git a/src/utils/function_cache.py b/src/utils/function_cache.py index d1deb02c73..23f7896f70 100755 --- a/src/utils/function_cache.py +++ b/src/utils/function_cache.py @@ -11,27 +11,34 @@ def cache(seconds=900): - def do_cache(f): def y(*args, **kwargs): - f_module = f.__module__.encode('utf-8') - f_name = f.__name__.encode('utf-8') - key = sha1(f_module + f_name + str(args).encode('UTF-8') + str(kwargs).encode('UTF-8')).hexdigest() + f_module = f.__module__.encode("utf-8") + f_name = f.__name__.encode("utf-8") + key = sha1( + f_module + + f_name + + str(args).encode("UTF-8") + + str(kwargs).encode("UTF-8") + ).hexdigest() result = django_cache.get(key) if result is None: result = f(*args, **kwargs) django_cache.set(key, result, seconds) return result + return y + return do_cache class mutable_cached_property(cached_property): - """ Expands django's cached property to allow property mutation + """Expands django's cached property to allow property mutation A property mutation (__set__) will clear the previously cached value """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fsetter = None @@ -46,4 +53,3 @@ def setter(self, fsetter): prop = type(self)(self.func, self.name) prop.setter = self.fsetter = fsetter return prop - diff --git a/src/utils/importer.py b/src/utils/importer.py index 7144aa97ea..d787d91f50 100755 --- a/src/utils/importer.py +++ b/src/utils/importer.py @@ -14,12 +14,12 @@ def clear_db(journal): - """ Deletes all articles, issues and all non-admin users from the specified journal. + """Deletes all articles, issues and all non-admin users from the specified journal. :param journal: the journal to act upon :return: None """ - print('Deleting all articles, issues and all non-admin users') + print("Deleting all articles, issues and all non-admin users") all_articles = models.Article.objects.filter(journal=journal) all_articles.delete() @@ -35,45 +35,45 @@ def clear_db(journal): def identify_journal_type_by_oai(url): - """ Attempts to identify the type of journal we are dealing with based on the OAI URL. + """Attempts to identify the type of journal we are dealing with based on the OAI URL. :param url: the URL to the OAI feed :return: a string containing the type of journal """ - if u'/jms/' in url: + if "/jms/" in url: return "UP" - if (u'?journal=' in url and u'?page=oai') or '/oai' in url: + if ("?journal=" in url and "?page=oai") or "/oai" in url: return "OJS" def import_ojs_article(**options): - """ Imports a single article from an Open Journal Systems installation + """Imports a single article from an Open Journal Systems installation :param options: a dictionary containing 'journal_id', 'user_id', 'url' and optionally a 'delete' flag :return: None """ - journal = journal_models.Journal.objects.get(pk=options['journal_id']) - user = core_models.Account.objects.get(pk=options['user_id']) - url = options['url'] + journal = journal_models.Journal.objects.get(pk=options["journal_id"]) + user = core_models.Account.objects.get(pk=options["user_id"]) + url = options["url"] - if options['delete']: + if options["delete"]: clear_db(journal) ojs.import_article(journal, user, url) def import_up_article(**options): - """ Imports a single article from a Ubiquity Press installation + """Imports a single article from a Ubiquity Press installation :param options: a dictionary containing 'journal_id', 'user_id', 'url' and optionally a 'delete' flag :return: None """ - journal = journal_models.Journal.objects.get(pk=options['journal_id']) - user = core_models.Account.objects.get(pk=options['user_id']) - url = options['url'] + journal = journal_models.Journal.objects.get(pk=options["journal_id"]) + user = core_models.Account.objects.get(pk=options["user_id"]) + url = options["url"] - if options['delete']: + if options["delete"]: clear_db(journal) update = options.get("update", False) @@ -81,7 +81,7 @@ def import_up_article(**options): def import_all(**options): - """ Imports all the content from a UP journal + """Imports all the content from a UP journal We overload the import_issue_images function setting load_missing, which will import all articles that have not yet been imported. @@ -90,70 +90,68 @@ def import_all(**options): :param options: a dictionary containing 'journal_id', 'user_id', and a 'url' :return: None """ - journal = journal_models.Journal.objects.get(code=options['journal_code']) - user = core_models.Account.objects.get(pk=options['user_id']) - url = options['url'] + journal = journal_models.Journal.objects.get(code=options["journal_code"]) + user = core_models.Account.objects.get(pk=options["user_id"]) + url = options["url"] update = options.get("update", False) - up.import_issue_images( - journal, user, url, import_missing=True, update=update) + up.import_issue_images(journal, user, url, import_missing=True, update=update) def import_issue_images(**options): - """ Imports a issue images and sequencing of sections/articles from a UP journal + """Imports a issue images and sequencing of sections/articles from a UP journal :param options: a dictionary containing 'journal_id', 'user_id', and a 'url' :return: None """ - journal = journal_models.Journal.objects.get(pk=options['journal_id']) - user = core_models.Account.objects.get(pk=options['user_id']) - url = options['url'] + journal = journal_models.Journal.objects.get(pk=options["journal_id"]) + user = core_models.Account.objects.get(pk=options["user_id"]) + url = options["url"] up.import_issue_images(journal, user, url, update=options.get("update")) def import_journal_metadata(**options): - """ Imports journal-level metadata from a UP journal + """Imports journal-level metadata from a UP journal :param options: a dictionary containing 'journal_id', 'user_id', and a 'url' :return: None """ - journal = journal_models.Journal.objects.get(pk=options['journal_id']) - user = core_models.Account.objects.get(pk=options['user_id']) - url = options['url'] + journal = journal_models.Journal.objects.get(pk=options["journal_id"]) + user = core_models.Account.objects.get(pk=options["user_id"]) + url = options["url"] up.import_journal_metadata(journal, user, url) def import_oai(**options): - """ Imports an OAI feed + """Imports an OAI feed :param options: a dictionary containing 'journal_id', 'user_id', 'url' and optionally a 'delete' flag :return: None """ requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - journal = journal_models.Journal.objects.get(pk=options['journal_id']) - user = core_models.Account.objects.get(pk=options['user_id']) + journal = journal_models.Journal.objects.get(pk=options["journal_id"]) + user = core_models.Account.objects.get(pk=options["user_id"]) - if options['delete']: + if options["delete"]: clear_db(journal) parse_OAI(journal, options, user) def parse_OAI(journal, options, user, resume=None): - if resume: - verb = '?verb=ListRecords&resumptionToken={0}'.format(resume) + verb = "?verb=ListRecords&resumptionToken={0}".format(resume) else: - verb = '?verb=ListRecords&metadataPrefix=oai_dc' + verb = "?verb=ListRecords&metadataPrefix=oai_dc" - url = options['url'] + verb + url = options["url"] + verb update = options.get("update", False) journal_type = identify_journal_type_by_oai(url) parsed_uri = urlparse(url) - domain = '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri) + domain = "{uri.scheme}://{uri.netloc}/".format(uri=parsed_uri) # note: we're not caching OAI pages as they update regularly page = requests.get(url, verify=False) @@ -169,13 +167,13 @@ def parse_OAI(journal, options, user, resume=None): print("Journal type currently unsupported") # see if there is a resumption token - resume = soup.find('resumptiontoken') + resume = soup.find("resumptiontoken") if resume: resume = resume.getText() - if resume and resume != '': - print('Executing resumeToken') + if resume and resume != "": + print("Executing resumeToken") parse_OAI(journal, options, user, resume=resume) return soup diff --git a/src/utils/importers/ojs.py b/src/utils/importers/ojs.py index 7bd7267ba6..0c4891f3b9 100755 --- a/src/utils/importers/ojs.py +++ b/src/utils/importers/ojs.py @@ -7,7 +7,7 @@ def import_article(journal, user, url): - """ Import an Open Journal Systems article. + """Import an Open Journal Systems article. :param journal: the journal to import to :param user: the user who will own the file @@ -16,7 +16,9 @@ def import_article(journal, user, url): """ # retrieve the remote page and establish if it has a DOI - already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists(url) + already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists( + url + ) if already_exists: # if here then this article has already been imported @@ -28,49 +30,57 @@ def import_article(journal, user, url): # get PDF and XML/HTML galleys pdf = shared.get_pdf_url(soup_object) - html = shared.get_soup(soup_object.find('meta', attrs={'name': 'citation_fulltext_html_url'}), 'content') + html = shared.get_soup( + soup_object.find("meta", attrs={"name": "citation_fulltext_html_url"}), + "content", + ) headers = { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/39.0.2171.95 Safari/537.36'} + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 Safari/537.36" + } if html: html = shared.requests.get(html, headers=headers) html_contents = BeautifulSoup(html.text, "lxml") - html_contents = html_contents.find('div', attrs={'id': 'content'}) + html_contents = html_contents.find("div", attrs={"id": "content"}) if html_contents: - html_contents = BeautifulSoup(html_contents.decode_contents(formatter=None), "lxml") - html_contents = html_contents.find('body') + html_contents = BeautifulSoup( + html_contents.decode_contents(formatter=None), "lxml" + ) + html_contents = html_contents.find("body") html_contents = html_contents.decode_contents(formatter=None) else: html_contents = None # attach the galleys to the new article galleys = { - 'PDF': pdf, + "PDF": pdf, } if html_contents: - galleys['HTML'] = html_contents + galleys["HTML"] = html_contents - shared.set_article_galleys_and_identifiers(doi, domain, galleys, new_article, url, user) + shared.set_article_galleys_and_identifiers( + doi, domain, galleys, new_article, url, user + ) # save the article to the database new_article.save() def import_oai(journal, user, soup): - """ Initiate an OAI import on an Open Journal Systems journal. + """Initiate an OAI import on an Open Journal Systems journal. :param journal: the journal to import to :param user: the user who will own imported articles :param soup: the BeautifulSoup object of the OAI feed :return: None """ - identifiers = soup.findAll('dc:identifier') + identifiers = soup.findAll("dc:identifier") for identifier in identifiers: - if identifier.contents[0].startswith('http'): - print('Parsing {0}'.format(identifier.contents[0])) + if identifier.contents[0].startswith("http"): + print("Parsing {0}".format(identifier.contents[0])) import_article(journal, user, identifier.contents[0]) diff --git a/src/utils/importers/shared.py b/src/utils/importers/shared.py index 4a897287e4..79ebe0324c 100755 --- a/src/utils/importers/shared.py +++ b/src/utils/importers/shared.py @@ -25,7 +25,9 @@ logger = get_logger(__name__) -def fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, galley_name="XML"): +def fetch_images_and_rewrite_xml_paths( + base, root, contents, article, user, galley_name="XML" +): """Download images from an XML or HTML document and rewrite the new galley to point to the correct source. :param base: a base URL for the remote journal install e.g. http://www.myjournal.org @@ -46,9 +48,9 @@ def fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, gall # add element:attribute properties here for images that should be downloaded and have their paths rewritten # so 'img':'src' means look for elements called 'img' with an attribute 'src' elements = { - 'img': 'src', - 'graphic': 'xlink:href', - 'inline-graphic': 'xlink:href', + "img": "src", + "graphic": "xlink:href", + "inline-graphic": "xlink:href", } # iterate over all found elements @@ -57,7 +59,6 @@ def fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, gall # iterate over all found elements of each type in the elements dictionary for idx, val in enumerate(images): - # attempt to pull a URL from the specified attribute url = get_soup(val, attribute) @@ -65,12 +66,14 @@ def fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, gall url_to_use = url # this is a Ubiquity Press-specific fix to rewrite the path so that we don't hit OJS's dud backend - if not url.startswith('/') and not url.startswith('http'): + if not url.startswith("/") and not url.startswith("http"): # Resolve article redirects before building image URLs real_root = requests.head(root, allow_redirects=True).url - url_to_use = real_root.replace('/article/view', '/articles') + '/' + url + url_to_use = ( + real_root.replace("/article/view", "/articles") + "/" + url + ) - #guess extension from url + # guess extension from url suffixes = Path(url_to_use).suffixes if suffixes: extension = "".join(suffixes) @@ -79,30 +82,49 @@ def fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, gall # download the image file try: - filename, mime = fetch_file(base, url_to_use, root, extension, article, user, handle_images=False) + filename, mime = fetch_file( + base, + url_to_use, + root, + extension, + article, + user, + handle_images=False, + ) except Exception as e: logger.error("[FIGURE IMPORT ERROR] %s" % val) continue # determine the MIME type and slice the first open bracket and everything after the comma off - mime = mime.split(',')[0][1:].replace("'", "") + mime = mime.split(",")[0][1:].replace("'", "") # store this image in the database affiliated with the new article - new_file = add_file(mime, extension, 'Galley image', user, filename, article, False) - absolute_new_filename = reverse('article_file_download', - kwargs={'identifier_type': 'id', 'identifier': article.id, - 'file_id': new_file.id}) + new_file = add_file( + mime, extension, "Galley image", user, filename, article, False + ) + absolute_new_filename = reverse( + "article_file_download", + kwargs={ + "identifier_type": "id", + "identifier": article.id, + "file_id": new_file.id, + }, + ) # rewrite the HTML or XML contents to point to the new image filename (a reverse lookup of # article_file_download) - print('Replacing image URL {0} with {1}'.format(url, absolute_new_filename)) + print( + "Replacing image URL {0} with {1}".format( + url, absolute_new_filename + ) + ) contents = str(contents).replace(url, absolute_new_filename) return contents def parse_date(date_string, is_iso): - """ Parse a date from a string according to timezone-specific settings + """Parse a date from a string according to timezone-specific settings :param date_string: the date string to be parsed :param is_iso: whether or not to use ISO-specific formatting settings ("%Y-%m-%dT%H:%M:%S" if True, otherwise @@ -111,17 +133,24 @@ def parse_date(date_string, is_iso): """ if date_string is not None and date_string != "": if is_iso: - return timezone.make_aware(datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S"), - timezone.get_current_timezone()) + return timezone.make_aware( + datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S"), + timezone.get_current_timezone(), + ) else: - return timezone.make_aware(datetime.strptime(date_string, "%Y-%m-%d"), timezone.get_current_timezone()) + return timezone.make_aware( + datetime.strptime(date_string, "%Y-%m-%d"), + timezone.get_current_timezone(), + ) else: print("Returning current datetime as no valid datetime was given") return timezone.now() -def fetch_file(base, url, root, extension, article, user, handle_images=False, auth_file=None): - """ Download a remote file and store in the database affiliated to a specific article +def fetch_file( + base, url, root, extension, article, user, handle_images=False, auth_file=None +): + """Download a remote file and store in the database affiliated to a specific article :param base: a base URL for the remote journal install e.g. http://www.myjournal.org :param url: either a full URL or a suffix to base that when concatenated will form a whole URL to the image @@ -136,28 +165,30 @@ def fetch_file(base, url, root, extension, article, user, handle_images=False, a requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # if this is not a full URL concatenate base and URL to form the full address - if not url.startswith('http'): + if not url.startswith("http"): url = base + url if not settings.SILENT_IMPORT_CACHE: - print('Fetching {0}'.format(url)) + print("Fetching {0}".format(url)) # imitate headers from a browser to avoid being blocked on some installs if auth_file: - resp, mime = utils_models.ImportCacheEntry.fetch(url, up_auth_file=auth_file, up_base_url=base) + resp, mime = utils_models.ImportCacheEntry.fetch( + url, up_auth_file=auth_file, up_base_url=base + ) else: resp, mime = utils_models.ImportCacheEntry.fetch(url=url) # If the function is not passed an extension, try to guess what it should be. if not extension: - extension = utils_shared.guess_extension(mime) or '.graphic' + extension = utils_shared.guess_extension(mime) or ".graphic" # set the filename to a unique UUID4 identifier with the passed file extension - filename = '{0}.{1}'.format(uuid4(), extension.lstrip(".")) + filename = "{0}.{1}".format(uuid4(), extension.lstrip(".")) # set the path to save to be the sub-directory for the article - path = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id)) + path = os.path.join(settings.BASE_DIR, "files", "articles", str(article.id)) # create the sub-folders as necessary if not os.path.exists(path): @@ -172,9 +203,9 @@ def fetch_file(base, url, root, extension, article, user, handle_images=False, a logger.warning("Cant extract images from %s" % url) if isinstance(resp, str): - resp = bytes(resp, 'utf-8') + resp = bytes(resp, "utf-8") - with open(os.path.join(path, filename), 'wb') as f: + with open(os.path.join(path, filename), "wb") as f: if not settings.SILENT_IMPORT_CACHE: print("Writing file {0} as binary".format(os.path.join(path, filename))) f.write(resp) @@ -183,8 +214,17 @@ def fetch_file(base, url, root, extension, article, user, handle_images=False, a return filename, mime -def save_file(base, contents, root, extension, article, user, handle_images=False, galley_name="XML"): - """ Save 'contents' to disk as a file associated with 'article' +def save_file( + base, + contents, + root, + extension, + article, + user, + handle_images=False, + galley_name="XML", +): + """Save 'contents' to disk as a file associated with 'article' :param base: a base URL for the remote journal install e.g. http://www.myjournal.org :param contents: the contents to be written to disk @@ -197,31 +237,42 @@ def save_file(base, contents, root, extension, article, user, handle_images=Fals """ # assign a unique UUID4 to be the filename - filename = '{0}.{1}'.format(uuid4(), extension) + filename = "{0}.{1}".format(uuid4(), extension) # set the path to the article's sub-folder - path = os.path.join(settings.BASE_DIR, 'files', 'articles', str(article.id)) + path = os.path.join(settings.BASE_DIR, "files", "articles", str(article.id)) # create the sub-folder structure if needed if not os.path.exists(path): os.makedirs(path, 0o0775) # write the file to disk - with open(os.path.join(path, filename), 'wb') as f: + with open(os.path.join(path, filename), "wb") as f: # process any images if instructed if handle_images: - contents = fetch_images_and_rewrite_xml_paths(base, root, contents, article, user, galley_name) + contents = fetch_images_and_rewrite_xml_paths( + base, root, contents, article, user, galley_name + ) if isinstance(contents, str): - contents = bytes(contents, 'utf8') + contents = bytes(contents, "utf8") f.write(contents) return filename -def add_file(file_mime, extension, description, owner, filename, article, galley=True, thumbnail=False): - """ Add a file to the File model in core. Saves a file to the database affiliated with an article. +def add_file( + file_mime, + extension, + description, + owner, + filename, + article, + galley=True, + thumbnail=False, +): + """Add a file to the File model in core. Saves a file to the database affiliated with an article. :param file_mime: the MIME type of the file. Used in serving the file back to users :param extension: the extension of the file @@ -236,10 +287,10 @@ def add_file(file_mime, extension, description, owner, filename, article, galley # create a new File object with the passed parameters - if extension.startswith('.'): - original_filename = 'file{0}'.format(extension) + if extension.startswith("."): + original_filename = "file{0}".format(extension) else: - original_filename = 'file.{0}'.format(extension) + original_filename = "file.{0}".format(extension) new_file = core_models.File( mime_type=file_mime, @@ -249,8 +300,8 @@ def add_file(file_mime, extension, description, owner, filename, article, galley description=description, owner=owner, is_galley=galley, - privacy='public', - article_id=article.pk + privacy="public", + article_id=article.pk, ) new_file.save() @@ -277,7 +328,7 @@ def add_file(file_mime, extension, description, owner, filename, article, galley def get_soup(soup_object, field_name, default=None): - """ Parses a soup object and returns field_name if found, otherwise default + """Parses a soup object and returns field_name if found, otherwise default :param soup_object: the BeautifulSoup object to parse :param field_name: the name of the field to look for @@ -291,16 +342,16 @@ def get_soup(soup_object, field_name, default=None): def parse_url(url): - """ Parses a URL into a well-formed and navigable format + """Parses a URL into a well-formed and navigable format :param url: the URL to parse :return: the formatted URL """ - return '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url)) + return "{uri.scheme}://{uri.netloc}/".format(uri=urlparse(url)) def fetch_page(url): - """ Fetches a remote page and returns a BeautifulSoup object + """Fetches a remote page and returns a BeautifulSoup object :param url: the URL to fetch :return: a BeautifulSoup object @@ -323,14 +374,13 @@ def extract_and_check_doi(soup_object): :return: a tuple of the doi and the identified item or False """ # see whether there's a DOI and, most importantly, whether it's a duplicate - doi = get_soup(soup_object.find('meta', attrs={'name': 'citation_doi'}), 'content') + doi = get_soup(soup_object.find("meta", attrs={"name": "citation_doi"}), "content") found = False if doi: try: identifier = identifiers_models.Identifier.objects.get( - id_type='doi', - identifier=doi + id_type="doi", identifier=doi ) found = identifier.article or False except identifiers_models.Identifier.DoesNotExist: @@ -340,30 +390,34 @@ def extract_and_check_doi(soup_object): def get_citation_info(soup_object): - citations = soup_object.find_all('div', attrs={"class": "citation-popup"}) + citations = soup_object.find_all("div", attrs={"class": "citation-popup"}) if citations: return citations[0].text return None def get_author_info(soup_object): - """ Extract authors, emails, and institutional affiliation from a BeautifulSoup object. + """Extract authors, emails, and institutional affiliation from a BeautifulSoup object. :param soup_object: a BeautifulSoup object of a page :return: a tuple of authors, emails, institutions and a boolean of whether we have emails for all authors """ - authors = soup_object.findAll('meta', attrs={'name': 'citation_author'}) + authors = soup_object.findAll("meta", attrs={"name": "citation_author"}) if not authors: - authors = soup_object.findAll('meta', attrs={'name': 'citation_authors'}) + authors = soup_object.findAll("meta", attrs={"name": "citation_authors"}) - emails = soup_object.findAll('meta', attrs={'name': 'citation_author_email'}) - institutions = soup_object.findAll('meta', attrs={'name': 'citation_author_institution'}) + emails = soup_object.findAll("meta", attrs={"name": "citation_author_email"}) + institutions = soup_object.findAll( + "meta", attrs={"name": "citation_author_institution"} + ) mismatch = len(authors) != len(emails) if mismatch: - print('Mismatch in number of authors, emails and institutions added. This article will not be ' - 'correctly attributed.') + print( + "Mismatch in number of authors, emails and institutions added. This article will not be " + "correctly attributed." + ) return authors, emails, institutions, mismatch @@ -376,37 +430,47 @@ def get_pdf_url(soup_object): :param soup_object: a BeautifulSoup object of a page :return: a string of the PDF URL """ - pdf = get_soup(soup_object.find('meta', attrs={'name': 'citation_pdf_url'}), 'content') + pdf = get_soup( + soup_object.find("meta", attrs={"name": "citation_pdf_url"}), "content" + ) if pdf: - pdf = pdf.replace('article/view/', 'article/viewFile/') + pdf = pdf.replace("article/view/", "article/viewFile/") return pdf def get_dates(soup_object, date_published_iso=False, date_submitted_iso=False): - """ Extracts publication dates from a BeautifulSoup object of a page. + """Extracts publication dates from a BeautifulSoup object of a page. :param soup_object: a BeautifulSoup object of a page :param date_published_iso: whether or not the date_published is in an ISO date format :param date_submitted_iso: whether or not the date_submitted is in an ISO date format :return: a tuple of the date published and the date submitted """ - pubbed = get_soup(soup_object.find('meta', attrs={'name': 'citation_publication_date'}), 'content') + pubbed = get_soup( + soup_object.find("meta", attrs={"name": "citation_publication_date"}), "content" + ) - if not pubbed or pubbed == '': - pubbed = get_soup(soup_object.find('meta', attrs={'name': 'DC.Date.issued'}), 'content') + if not pubbed or pubbed == "": + pubbed = get_soup( + soup_object.find("meta", attrs={"name": "DC.Date.issued"}), "content" + ) date_published = parse_date(pubbed, date_published_iso) - date_submitted = parse_date(get_soup(soup_object.find('meta', attrs={'name': 'DC.Date.dateSubmitted'}), 'content'), - date_submitted_iso) + date_submitted = parse_date( + get_soup( + soup_object.find("meta", attrs={"name": "DC.Date.dateSubmitted"}), "content" + ), + date_submitted_iso, + ) return date_published, date_submitted def create_new_article(date_published, date_submitted, journal, soup_object, user): - """ Create a new article in the database. + """Create a new article in the database. :param date_published: the date the article was published :param date_submitted: the date the article was submitted @@ -416,21 +480,34 @@ def create_new_article(date_published, date_submitted, journal, soup_object, use :return: a tuple of a dictionary information about the article and the new article object """ import html - abstract_html = get_soup(soup_object.find('meta', attrs={'name': 'DC.Description'}), 'content', ''), + + abstract_html = ( + get_soup( + soup_object.find("meta", attrs={"name": "DC.Description"}), "content", "" + ), + ) abstract = html.unescape(abstract_html) article_dict = { - 'title': get_soup(soup_object.find('meta', attrs={'name': 'DC.Title'}), 'content'), - 'abstract': abstract[0], - 'language': get_soup(soup_object.find('meta', attrs={'name': 'DC.Language'}), 'content'), - 'date_published': date_published, - 'date_submitted': date_submitted, - 'journal': journal, - 'owner': user, - 'stage': "Published", - 'current_step': 5, - 'page_numbers': get_soup(soup_object.find('meta', attrs={'name': 'DC.Identifier.pageNumber'}), 'content', ''), - 'is_import': True, + "title": get_soup( + soup_object.find("meta", attrs={"name": "DC.Title"}), "content" + ), + "abstract": abstract[0], + "language": get_soup( + soup_object.find("meta", attrs={"name": "DC.Language"}), "content" + ), + "date_published": date_published, + "date_submitted": date_submitted, + "journal": journal, + "owner": user, + "stage": "Published", + "current_step": 5, + "page_numbers": get_soup( + soup_object.find("meta", attrs={"name": "DC.Identifier.pageNumber"}), + "content", + "", + ), + "is_import": True, } if not article_dict.get("title"): article_dict["title"] = "# No Title found #" @@ -440,8 +517,10 @@ def create_new_article(date_published, date_submitted, journal, soup_object, use return article_dict, new_article -def set_article_attributions(authors, emails, institutions, mismatch, article, citation=None): - """ Set author, email, and institution information on an article +def set_article_attributions( + authors, emails, institutions, mismatch, article, citation=None +): + """Set author, email, and institution information on an article :param authors: the authors of the article :param emails: the authors' emails @@ -454,28 +533,28 @@ def set_article_attributions(authors, emails, institutions, mismatch, article, c fetch_emails = not mismatch for idx, val in enumerate(authors): - author_name = get_soup(val, 'content') + author_name = get_soup(val, "content") - if ',' in author_name: - split_author = author_name.split(', ', 1) - author_name = '{0} {1}'.format(split_author[1], split_author[0]) + if "," in author_name: + split_author = author_name.split(", ", 1) + author_name = "{0} {1}".format(split_author[1], split_author[0]) if fetch_emails: - email = get_soup(emails[idx], 'content') + email = get_soup(emails[idx], "content") if email == "journal@openlibhums.org": # Generate Janeway compatible dummy email email = utils_shared.generate_password(16) - email = u"{0}@{1}".format(email, settings.DUMMY_EMAIL_DOMAIN) + email = "{0}@{1}".format(email, settings.DUMMY_EMAIL_DOMAIN) else: # if there are a bad number of emails, we will automatically generate one email = utils_shared.generate_password(16) - email = u"{0}@journal.org".format(email) + email = "{0}@journal.org".format(email) if len(authors) == len(institutions): - institution = get_soup(institutions[idx], 'content') + institution = get_soup(institutions[idx], "content") else: # if no institution, simply set to blank - institution = '' + institution = "" # add an account for this new user account = core_models.Account.objects.filter(email__iexact=email) @@ -502,27 +581,27 @@ def set_article_attributions(authors, emails, institutions, mismatch, article, c o, c = submission_models.ArticleAuthorOrder.objects.get_or_create( article=article, author=account, - defaults={'order': article.next_author_sort()}, + defaults={"order": article.next_author_sort()}, ) # Copy behaviour of snapshot_self, some authors might have a # shared dummy email address. f, created = submission_models.FrozenAuthor.objects.get_or_create( **{ - 'article': article, - 'first_name': parsed_name["first_name"], - 'middle_name': parsed_name["middle_name"], - 'last_name': parsed_name["last_name"], - 'institution': institution, - 'order': o.order, - 'defaults': {"author": account}, - + "article": article, + "first_name": parsed_name["first_name"], + "middle_name": parsed_name["middle_name"], + "last_name": parsed_name["last_name"], + "institution": institution, + "order": o.order, + "defaults": {"author": account}, }, ) - -def set_article_section(article, soup_object, element='h4', attributes=None, default='Articles'): - """ Set an article to a specific section +def set_article_section( + article, soup_object, element="h4", attributes=None, default="Articles" +): + """Set an article to a specific section :param article: the article in question :param soup_object: a BeautifulSoup object of a page from which to extract section information @@ -534,7 +613,7 @@ def set_article_section(article, soup_object, element='h4', attributes=None, def # set the default section name here if attributes is None: - attributes = {'class': 'main-color-text'} + attributes = {"class": "main-color-text"} section_name = default @@ -545,17 +624,19 @@ def set_article_section(article, soup_object, element='h4', attributes=None, def pass # either attribute the section or notify the user that we are using the default - if section_name and section_name != '': - print('Adding article to section {0}'.format(section_name)) + if section_name and section_name != "": + print("Adding article to section {0}".format(section_name)) - section, created = submission_models.Section.objects.get_or_create(journal=article.journal, name=section_name) + section, created = submission_models.Section.objects.get_or_create( + journal=article.journal, name=section_name + ) article.section = section else: print('No section information found. Reverting to default of "Articles"') def set_article_issue_and_volume(article, soup_object, date_published): - """ Set the article's issue and volume + """Set the article's issue and volume :param article: the article in question :param soup_object: a BeautifulSoup object of a page @@ -566,14 +647,24 @@ def set_article_issue_and_volume(article, soup_object, date_published): journal=article.journal, code="issue", ) - issue = get_soup(soup_object.find('meta', attrs={'name': 'citation_issue'}), 'content', 0) - volume = int(get_soup(soup_object.find('meta', attrs={'name': 'citation_volume'}), 'content', 0)) + issue = get_soup( + soup_object.find("meta", attrs={"name": "citation_issue"}), "content", 0 + ) + volume = int( + get_soup( + soup_object.find("meta", attrs={"name": "citation_volume"}), "content", 0 + ) + ) # Try DC tags if not issue: - dc_issue = get_soup(soup_object.find('meta', attrs={'name': 'DC.Source.Issue'}), 'content', "") + dc_issue = get_soup( + soup_object.find("meta", attrs={"name": "DC.Source.Issue"}), "content", "" + ) if not volume: - dc_volume = get_soup(soup_object.find('meta', attrs={'name': 'DC.Source.Volume'}), 'content', "") + dc_volume = get_soup( + soup_object.find("meta", attrs={"name": "DC.Source.Volume"}), "content", "" + ) if dc_volume.isdigit(): volume = int(dc_volume) @@ -591,29 +682,31 @@ def set_article_issue_and_volume(article, soup_object, date_published): if created: new_issue.save() log_string = "Created a new issue ({0}:{1}, {2})".format( - volume, issue, date_published) + volume, issue, date_published + ) logger.info(log_string) def set_article_keywords(article, soup_object): - keyword_string = (get_soup( - soup_object.find('meta', attrs={'name': 'citation_keywords'}), - 'content', - )) + keyword_string = get_soup( + soup_object.find("meta", attrs={"name": "citation_keywords"}), + "content", + ) if keyword_string: for i, word in enumerate(keyword_string.split(";")): if word: - keyword, created = submission_models.Keyword.objects \ - .get_or_create(word=word.lstrip()) + keyword, created = submission_models.Keyword.objects.get_or_create( + word=word.lstrip() + ) submission_models.KeywordArticle.objects.update_or_create( article=article, keyword=keyword, - defaults = {"order": i}, + defaults={"order": i}, ) def set_article_galleys(domain, galleys, article, url, user): - """ Attach a set of remote galley files to the local article + """Attach a set of remote galley files to the local article :param domain: the formatted domain object for the remote file :param galleys: a dictionary of named galley URLs to harvest @@ -628,31 +721,60 @@ def set_article_galleys(domain, galleys, article, url, user): for galley_name, galley in galleys.items(): if galley: - if galley_name == 'PDF' or galley_name == 'XML': - handle_images = True if galley_name == 'XML' else False - - filename, mime = fetch_file(domain, galley, url, galley_name.lower(), article, user, - handle_images=handle_images) - add_file('application/{0}'.format(galley_name.lower()), galley_name.lower(), - 'Galley {0}'.format(galley_name), user, filename, article) + if galley_name == "PDF" or galley_name == "XML": + handle_images = True if galley_name == "XML" else False + + filename, mime = fetch_file( + domain, + galley, + url, + galley_name.lower(), + article, + user, + handle_images=handle_images, + ) + add_file( + "application/{0}".format(galley_name.lower()), + galley_name.lower(), + "Galley {0}".format(galley_name), + user, + filename, + article, + ) else: # assuming that this is HTML, which we save to disk rather than fetching - handle_images = True if galley_name == 'HTML' else False - filename = save_file(domain, galley, url, galley_name.lower(), article, user, - handle_images=handle_images, galley_name=galley_name) - add_file('text/{0}'.format(galley_name.lower()), galley_name.lower(), - 'Galley {0}'.format(galley_name), user, filename, article) + handle_images = True if galley_name == "HTML" else False + filename = save_file( + domain, + galley, + url, + galley_name.lower(), + article, + user, + handle_images=handle_images, + galley_name=galley_name, + ) + add_file( + "text/{0}".format(galley_name.lower()), + galley_name.lower(), + "Galley {0}".format(galley_name), + user, + filename, + article, + ) def set_article_identifier(doi, article): - """ Save an identifier to the article + """Save an identifier to the article :param doi: the DOI to save :param article: the article on which to act :return: None """ if doi: - identifier = identifiers_models.Identifier.objects.create(id_type='doi', identifier=doi, article=article) + identifier = identifiers_models.Identifier.objects.create( + id_type="doi", identifier=doi, article=article + ) identifier.save() print("Article imported with ID: {0}".format(doi)) else: @@ -660,7 +782,7 @@ def set_article_identifier(doi, article): def fetch_page_and_check_if_exists(url): - """ Fetch a remote URL and check if the DOI already exists + """Fetch a remote URL and check if the DOI already exists :param url: the URL of the remote page :return: tuple of whether the DOI already exists locally, the DOI, the formatted domain, and the BeautifulSoup @@ -674,8 +796,10 @@ def fetch_page_and_check_if_exists(url): return already_exists, doi, domain, soup_object -def get_and_set_metadata(journal, soup_object, user, date_published_iso, date_submitted_iso): - """ Fetch article metadata and attach it to the article +def get_and_set_metadata( + journal, soup_object, user, date_published_iso, date_submitted_iso +): + """Fetch article metadata and attach it to the article :param journal: the journal to which the article should belong :param soup_object: a BeautifulSoup object of the page @@ -686,13 +810,20 @@ def get_and_set_metadata(journal, soup_object, user, date_published_iso, date_su """ authors, emails, institutions, mismatch = get_author_info(soup_object) - date_published, date_submitted = get_dates(soup_object, date_published_iso=date_published_iso, - date_submitted_iso=date_submitted_iso) + date_published, date_submitted = get_dates( + soup_object, + date_published_iso=date_published_iso, + date_submitted_iso=date_submitted_iso, + ) - article_dict, new_article = create_new_article(date_published, date_submitted, journal, soup_object, user) + article_dict, new_article = create_new_article( + date_published, date_submitted, journal, soup_object, user + ) citation = get_citation_info(soup_object) - set_article_attributions(authors, emails, institutions, mismatch, new_article, citation) + set_article_attributions( + authors, emails, institutions, mismatch, new_article, citation + ) set_article_section(new_article, soup_object) set_article_issue_and_volume(new_article, soup_object, date_published) set_article_keywords(new_article, soup_object) @@ -701,7 +832,7 @@ def get_and_set_metadata(journal, soup_object, user, date_published_iso, date_su def set_article_galleys_and_identifiers(doi, domain, galleys, article, url, user): - """ Set the galleys and identifiers on an article + """Set the galleys and identifiers on an article :param doi: the DOI :param domain: the formatted URL domain string @@ -738,9 +869,9 @@ def get_user_profile(soup): if a_elem: email = fetch_email_from_href(a_elem) if email: - author_dict['email'] = email + author_dict["email"] = email - if cell_0 == 'Name' and author_dict: + if cell_0 == "Name" and author_dict: authors.append(author_dict) author_dict = dict() @@ -754,7 +885,7 @@ def get_user_profile(soup): def fetch_email_from_href(a_soup): - href = urllib.parse.unquote(a_soup.attrs['href']) + href = urllib.parse.unquote(a_soup.attrs["href"]) email_regex = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)" @@ -767,7 +898,7 @@ def fetch_email_from_href(a_soup): def parse_author_names(author, citation=None): - """ Parses parts of the name from input string checking against citation + """Parses parts of the name from input string checking against citation Can distinguish middle names from multi word last_names :param author: A single string containing the author's full name @@ -805,9 +936,9 @@ def parse_author_names(author, citation=None): } -SEED_KEYS = [ - "First Name", "Middle Name", "Last Name", "Affiliation", "Country" -] +SEED_KEYS = ["First Name", "Middle Name", "Last Name", "Affiliation", "Country"] + + def generate_dummy_email(profile_dict): seed = sum(profile_dict.get(key, "") for key in SEED_KEYS) hashed = hashlib.md5(str(seed).encode("utf-8")).hexdigest() diff --git a/src/utils/importers/up.py b/src/utils/importers/up.py index 073d3a5006..3f638865f5 100755 --- a/src/utils/importers/up.py +++ b/src/utils/importers/up.py @@ -32,55 +32,70 @@ def get_thumbnails_url(url): - """ Extract thumbnails URL from a Ubiquity Press site. + """Extract thumbnails URL from a Ubiquity Press site. :param url: the base URL of the journal :return: the thumbnail URL for this journal """ logger.info("Extracting thumbnails URL.") - section_filters = ["f=%d" % i for i in range(1,10)] + section_filters = ["f=%d" % i for i in range(1, 10)] flt = "&".join(section_filters) - url_to_use = url + '/articles/?' + flt + '&order=date_published&app=1000' + url_to_use = url + "/articles/?" + flt + "&order=date_published&app=1000" resp, mime = utils_models.ImportCacheEntry.fetch(url=url_to_use) soup = BeautifulSoup(resp) - article = soup.find('div', attrs={'class': 'article-image'}) + article = soup.find("div", attrs={"class": "article-image"}) article = BeautifulSoup(str(article)) - id_href = shared.get_soup(article.find('img'), 'src') + id_href = shared.get_soup(article.find("img"), "src") - if id_href.endswith('/'): + if id_href.endswith("/"): id_href = id_href[:-1] - id_href_split = id_href.split('/') + id_href_split = id_href.split("/") id_href = id_href_split[:-1] - id_href = '/'.join(id_href)[1:] + id_href = "/".join(id_href)[1:] return id_href def import_article_images(journal, user, url, thumb_path=None, update=True): url = requests.head(url, allow_redirects=True).url - already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists(url) + already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists( + url + ) article = already_exists # rip XML out if found - pattern = re.compile('.*?XML.*') - xml = soup_object.find('a', text=pattern) + pattern = re.compile(".*?XML.*") + xml = soup_object.find("a", text=pattern) galley_name = "XML" if article and xml: article.galley_set.filter(type="xml").delete() logger.info("Ripping XML") - xml = xml.get('href', None).strip() + xml = xml.get("href", None).strip() galley = xml handle_images = True - filename, mime = shared.fetch_file(domain, galley, url, galley_name.lower(), article, user, - handle_images=handle_images) - shared.add_file('application/{0}'.format(galley_name.lower()), galley_name.lower(), - 'Galley {0}'.format(galley_name), user, filename, article) + filename, mime = shared.fetch_file( + domain, + galley, + url, + galley_name.lower(), + article, + user, + handle_images=handle_images, + ) + shared.add_file( + "application/{0}".format(galley_name.lower()), + galley_name.lower(), + "Galley {0}".format(galley_name), + user, + filename, + article, + ) def import_article(journal, user, url, thumb_path=None, update=False): - """ Import a Ubiquity Press article. + """Import a Ubiquity Press article. :param journal: the journal to import to :param user: the user who will own the file @@ -90,7 +105,9 @@ def import_article(journal, user, url, thumb_path=None, update=False): """ # retrieve the remote page and establish if it has a DOI - already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists(url) + already_exists, doi, domain, soup_object = shared.fetch_page_and_check_if_exists( + url + ) requests.packages.urllib3.disable_warnings(InsecureRequestWarning) @@ -102,22 +119,25 @@ def import_article(journal, user, url, thumb_path=None, update=False): article = shared.get_and_set_metadata(journal, soup_object, user, False, True) # try to do a license lookup - pattern = re.compile(r'creativecommons') + pattern = re.compile(r"creativecommons") license_object = [] - license_tag = soup_object.find(href=pattern) or '' + license_tag = soup_object.find(href=pattern) or "" if license_tag: license_object = models.Licence.objects.filter( - url=license_tag['href'].replace('http:', 'https:'), journal=journal) + url=license_tag["href"].replace("http:", "https:"), journal=journal + ) if len(license_object) > 0 and license_object[0] is not None: license_object = license_object[0] - logger.info("Found a license for this article: {0}".format( - license_object.short_name)) + logger.info( + "Found a license for this article: {0}".format(license_object.short_name) + ) article.license = license_object if not article.license: license_object = models.Licence.objects.get( - name='All rights reserved', journal=journal, + name="All rights reserved", + journal=journal, ) logger.warning( "Did not find a license for this article. Using: {0}".format( @@ -132,12 +152,12 @@ def import_article(journal, user, url, thumb_path=None, update=False): article.custom_how_to_cite = how_to_cite # determine if the article is peer reviewed - peer_reviewed = soup_object.find(name='a', text='Peer Reviewed') is not None + peer_reviewed = soup_object.find(name="a", text="Peer Reviewed") is not None if not peer_reviewed: # Check credit-block if peer reviewed is not a link peer_reviewed = any( "Peer Reviewed" in div.text - for div in soup_object.find_all('div', class_="credit-block") + for div in soup_object.find_all("div", class_="credit-block") ) logger.debug("Peer reviewed: {0}".format(peer_reviewed)) @@ -147,28 +167,24 @@ def import_article(journal, user, url, thumb_path=None, update=False): pdf = shared.get_pdf_url(soup_object) # rip XML out if found - pattern = re.compile('.*?XML.*') - xml = soup_object.find('a', text=pattern) + pattern = re.compile(".*?XML.*") + xml = soup_object.find("a", text=pattern) html = None if xml: logger.info("Ripping XML") - xml = xml.get('href', None).strip() + xml = xml.get("href", None).strip() else: # looks like there isn't any XML # instead we'll pull out any div with an id of "xml-article" and add as an HTML galley logger.info("Ripping HTML") - html = soup_object.find('div', attrs={'id': 'xml-article'}) + html = soup_object.find("div", attrs={"id": "xml-article"}) if html: html = str(html.contents[0]) # attach the galleys to the new article - galleys = { - 'PDF': pdf, - 'XML': xml, - 'HTML': html - } + galleys = {"PDF": pdf, "XML": xml, "HTML": html} shared.set_article_galleys(domain, galleys, article, url, user) if not already_exists: # The code below is not safe for updates @@ -184,24 +200,34 @@ def import_article(journal, user, url, thumb_path=None, update=False): if url.endswith("/"): url = url[:-1] - final_path_element = url.split('/')[-1] - id_regex = re.compile(r'.*?(\d+)') + final_path_element = url.split("/")[-1] + id_regex = re.compile(r".*?(\d+)") matches = id_regex.match(final_path_element) try: article_id = matches.group(1) logger.info("Determined remote article ID as: {0}".format(article_id)) - logger.info("Thumbnail path: {thumb_path}, URL: {url}".format( - thumb_path=thumb_path, url=url)) + logger.info( + "Thumbnail path: {thumb_path}, URL: {url}".format( + thumb_path=thumb_path, url=url + ) + ) filename, mime = shared.fetch_file( domain, - thumb_path + "/" + article_id, "", - 'graphic', - article, user, + thumb_path + "/" + article_id, + "", + "graphic", + article, + user, ) shared.add_file( - mime, 'graphic', 'Thumbnail', user, filename, article, + mime, + "graphic", + "Thumbnail", + user, + filename, + article, thumbnail=True, ) except AttributeError: @@ -212,11 +238,12 @@ def import_article(journal, user, url, thumb_path=None, update=False): article.save() # lookup stats - stats = soup_object.findAll('div', {'class': 'stat-number'}) + stats = soup_object.findAll("div", {"class": "stat-number"}) try: if stats and (not already_exists or update): from metrics import models as metrics_models + views = stats[0].contents[0] if len(stats) > 1: downloads = stats[1].contents[0] @@ -224,10 +251,11 @@ def import_article(journal, user, url, thumb_path=None, update=False): downloads = 0 o, _ = metrics_models.HistoricArticleAccess.objects.get_or_create( - article=article) + article=article + ) o.downloads = downloads - o.views=views + o.views = views o.save() except (IndexError, AttributeError): logger.info("No article metrics found") @@ -236,7 +264,7 @@ def import_article(journal, user, url, thumb_path=None, update=False): def import_how_to_cite(soup): - """ Extracts the 'How to cite' section of an article + """Extracts the 'How to cite' section of an article The text can be found under a span with the class 'span-citation', however this class is used in other contexts. In order to detect the right one, @@ -259,28 +287,31 @@ def import_how_to_cite(soup): def import_oai(journal, user, soup, domain, update=False): - """ Initiate an OAI import on a Ubiquity Press journal. + """Initiate an OAI import on a Ubiquity Press journal. - :param journal: the journal to import to - :param user: the user who will own imported articles - :param soup: the BeautifulSoup object of the OAI feed - :param domain: the domain of the journal (for extracting thumbnails) - :return: None - """ + :param journal: the journal to import to + :param user: the user who will own imported articles + :param soup: the BeautifulSoup object of the OAI feed + :param domain: the domain of the journal (for extracting thumbnails) + :return: None + """ thumb_path = get_thumbnails_url(domain) - identifiers = soup.findAll('dc:identifier') + identifiers = soup.findAll("dc:identifier") for identifier in identifiers: # rewrite the phrase /jms in Ubiquity Press OAI feeds to get version with # full and proper email metadata - identifier.contents[0] = identifier.contents[0].replace('/jms', '') - if identifier.contents[0].startswith('http'): - logger.info('Parsing {0}'.format(identifier.contents[0])) + identifier.contents[0] = identifier.contents[0].replace("/jms", "") + if identifier.contents[0].startswith("http"): + logger.info("Parsing {0}".format(identifier.contents[0])) import_article( - journal, user, identifier.contents[0], thumb_path, + journal, + user, + identifier.contents[0], + thumb_path, update=update, ) @@ -291,8 +322,8 @@ def import_oai(journal, user, soup, domain, update=False): def import_journal_metadata(journal, user, url): base_url = url - issn = re.compile(r'E-ISSN: (\d{4}-\d{4})') - publisher = re.compile(r'Published by (.*)') + issn = re.compile(r"E-ISSN: (\d{4}-\d{4})") + publisher = re.compile(r"Published by (.*)") requests.packages.urllib3.disable_warnings(InsecureRequestWarning) @@ -300,18 +331,18 @@ def import_journal_metadata(journal, user, url): resp, mime = utils_models.ImportCacheEntry.fetch(url=base_url) - soup = BeautifulSoup(resp, 'lxml') + soup = BeautifulSoup(resp, "lxml") issn_result = soup.find(text=issn) issn_match = issn.match(str(issn_result).strip()) - logger.info('ISSN set to: {0}'.format(issn_match.group(1))) + logger.info("ISSN set to: {0}".format(issn_match.group(1))) journal.issn = issn_match.group(1) try: publisher_result = soup.find(text=publisher) publisher_match = str(publisher_result.next_sibling.getText()).strip() - logger.info('Publisher set to: {0}'.format(publisher_match)) + logger.info("Publisher set to: {0}".format(publisher_match)) journal.publisher = publisher_match journal.save() except Exception as e: @@ -319,16 +350,18 @@ def import_journal_metadata(journal, user, url): def parse_backend_list(url, auth_file, auth_url, regex): - html_body, mime = utils_models.ImportCacheEntry.fetch(url, up_base_url=auth_url, up_auth_file=auth_file) + html_body, mime = utils_models.ImportCacheEntry.fetch( + url, up_base_url=auth_url, up_auth_file=auth_file + ) matches = re.findall(regex, html_body.decode()) # look for next_page - soup_object = BeautifulSoup(html_body, 'lxml') - soup = soup_object.find(text='>') + soup_object = BeautifulSoup(html_body, "lxml") + soup = soup_object.find(text=">") if soup: - href = soup.parent.attrs['href'] + href = soup.parent.attrs["href"] matches += parse_backend_list(href, auth_file, auth_url, regex) return matches @@ -337,20 +370,20 @@ def parse_backend_list(url, auth_file, auth_url, regex): def get_article_list(url, list_type, auth_file): auth_url = url - regex = '\/jms\/editor\/submissionReview\/(\d+)' - - if list_type == 'in_review': - url += '/jms/editor/submissions/submissionsInReview' - regex = '\/jms\/editor\/submissionReview\/(\d+)' - elif list_type == 'unassigned': - url += '/jms/editor/submissions/submissionsUnassigned' - regex = '\/jms\/editor\/submission\/(\d+)' - elif list_type == 'in_editing': - url += '/jms/editor/submissions/submissionsInEditing' - regex = '\/jms\/editor\/submissionEditing\/(\d+)' - elif list_type == 'archive': - url += '/jms/editor/submissions/submissionsArchives' - regex = '\/jms\/editor\/submissionEditing\/(\d+)' + regex = "\/jms\/editor\/submissionReview\/(\d+)" + + if list_type == "in_review": + url += "/jms/editor/submissions/submissionsInReview" + regex = "\/jms\/editor\/submissionReview\/(\d+)" + elif list_type == "unassigned": + url += "/jms/editor/submissions/submissionsUnassigned" + regex = "\/jms\/editor\/submission\/(\d+)" + elif list_type == "in_editing": + url += "/jms/editor/submissions/submissionsInEditing" + regex = "\/jms\/editor\/submissionEditing\/(\d+)" + elif list_type == "archive": + url += "/jms/editor/submissions/submissionsArchives" + regex = "\/jms\/editor\/submissionEditing\/(\d+)" else: return None @@ -360,16 +393,18 @@ def get_article_list(url, list_type, auth_file): def parse_backend_user_list(url, auth_file, auth_url, regex): - html_body, mime = utils_models.ImportCacheEntry.fetch(url, up_base_url=auth_url, up_auth_file=auth_file) + html_body, mime = utils_models.ImportCacheEntry.fetch( + url, up_base_url=auth_url, up_auth_file=auth_file + ) matches = re.findall(regex, html_body.decode()) # look for next_page - soup_object = BeautifulSoup(html_body, 'lxml') - soup = soup_object.find(text='>') + soup_object = BeautifulSoup(html_body, "lxml") + soup = soup_object.find(text=">") if soup: - href = soup.parent.attrs['href'] + href = soup.parent.attrs["href"] matches += parse_backend_user_list(href, auth_file, auth_url, regex) return matches @@ -378,8 +413,8 @@ def parse_backend_user_list(url, auth_file, auth_url, regex): def get_user_list(url, auth_file): auth_url = url - url += '/jms/manager/people/all' - regex = '\/manager\/userProfile\/(\d+)' + url += "/jms/manager/people/all" + regex = "\/manager\/userProfile\/(\d+)" matches = parse_backend_user_list(url, auth_file, auth_url, regex) @@ -388,17 +423,17 @@ def get_user_list(url, auth_file): def map_review_recommendation(recommentdation): recommendations = { - '2': ED.MINOR_REVISIONS.value, - '3': ED.MAJOR_REVISIONS.value, - '5': ED.REJECT.value, - '1': ED.ACCEPT.value, + "2": ED.MINOR_REVISIONS.value, + "3": ED.MAJOR_REVISIONS.value, + "5": ED.REJECT.value, + "1": ED.ACCEPT.value, } return recommendations.get(recommentdation, None) def import_issue_images(journal, user, url, import_missing=False, update=False): - """ Imports all issue images and other issue related content + """Imports all issue images and other issue related content Currently also reorders all issues, articles and sections within issues, article thumbnails and issue titles. :param journal: a journal.models.Journal @@ -409,14 +444,14 @@ def import_issue_images(journal, user, url, import_missing=False, update=False): """ base_url = url - if not url.endswith('/issue/archive/'): - url += '/issue/archive/' + if not url.endswith("/issue/archive/"): + url += "/issue/archive/" requests.packages.urllib3.disable_warnings(InsecureRequestWarning) resp, mime = utils_models.ImportCacheEntry.fetch(url=url) - soup = BeautifulSoup(resp, 'lxml') + soup = BeautifulSoup(resp, "lxml") from django.conf import settings import os @@ -424,79 +459,97 @@ def import_issue_images(journal, user, url, import_missing=False, update=False): for issue in journal.issues.filter(issue_type__code="issue"): issue_num = issue.issue - pattern = re.compile(r'\/\d+\/volume\/{0}\/issue\/{1}'.format( - issue.volume, issue_num)) + pattern = re.compile( + r"\/\d+\/volume\/{0}\/issue\/{1}".format(issue.volume, issue_num) + ) img_url_suffix = soup.find(src=pattern) if img_url_suffix: - img_url = base_url + img_url_suffix.get('src') + img_url = base_url + img_url_suffix.get("src") logger.info("Fetching {0}".format(img_url)) resp, mime = utils_models.ImportCacheEntry.fetch(url=img_url) - path = os.path.join(settings.BASE_DIR, 'files', 'journals', str(journal.id)) + path = os.path.join(settings.BASE_DIR, "files", "journals", str(journal.id)) os.makedirs(path, exist_ok=True) - path = os.path.join(path, 'volume{0}_issue_{0}.graphic'.format(issue.volume, issue_num)) + path = os.path.join( + path, "volume{0}_issue_{0}.graphic".format(issue.volume, issue_num) + ) - with open(path, 'wb') as f: + with open(path, "wb") as f: f.write(resp) - with open(path, 'rb') as f: + with open(path, "rb") as f: issue.cover_image.save(path, File(f)) - sequence_pattern = re.compile(r'.*?(\d+)\/volume\/{0}\/issue\/{1}.*'.format(issue.volume, issue_num)) + sequence_pattern = re.compile( + r".*?(\d+)\/volume\/{0}\/issue\/{1}.*".format(issue.volume, issue_num) + ) issue.order = int(sequence_pattern.match(img_url).group(1)) - logger.info("Setting Volume {0}, Issue {1} sequence to: {2}".format(issue.volume, issue_num, issue.order)) + logger.info( + "Setting Volume {0}, Issue {1} sequence to: {2}".format( + issue.volume, issue_num, issue.order + ) + ) logger.info("Extracting section orders within the issue...") - new_url = '/{0}/volume/{1}/issue/{2}/'.format(issue.order, issue.volume, issue_num) + new_url = "/{0}/volume/{1}/issue/{2}/".format( + issue.order, issue.volume, issue_num + ) resp, mime = utils_models.ImportCacheEntry.fetch(url=base_url + new_url) - soup_issue = BeautifulSoup(resp, 'lxml') + soup_issue = BeautifulSoup(resp, "lxml") - sections_to_order = soup_issue.find_all(name='h2', attrs={'class': 'main-color-text'}) + sections_to_order = soup_issue.find_all( + name="h2", attrs={"class": "main-color-text"} + ) # Find issue title try: - issue_title = soup_issue.find("div", {"class": "multi-inline"}).find("h1").string + issue_title = ( + soup_issue.find("div", {"class": "multi-inline"}).find("h1").string + ) issue_title = issue_title.strip(" -\n") if issue.issue_title and issue_title not in issue.issue_title: - issue.issue_title = "{} - {}".format( - issue_title, issue.issue_title) + issue.issue_title = "{} - {}".format(issue_title, issue.issue_title) else: issue.issue_title = issue_title except AttributeError as e: logger.debug("Couldn't find an issue title: %s" % e) - #Find issue description + # Find issue description try: - desc_parts = soup_issue.find("div", {"class": "article-type-list-block"}).findAll("p", {"class": "p1"}) + desc_parts = soup_issue.find( + "div", {"class": "article-type-list-block"} + ).findAll("p", {"class": "p1"}) issue.issue_description = "\n".join(str(p) for p in desc_parts) except AttributeError as e: logger.debug("Couldn't extract an issue description %s" % e) - - sections_to_order = soup_issue.find_all(name='h2', attrs={'class': 'main-color-text'}) + sections_to_order = soup_issue.find_all( + name="h2", attrs={"class": "main-color-text"} + ) # delete existing order models for sections for this issue journal_models.SectionOrdering.objects.filter(issue=issue).delete() for section_order, section in enumerate(sections_to_order): - - logger.info('[{0}] {1}'.format(section_order, section.getText())) + logger.info("[{0}] {1}".format(section_order, section.getText())) order_section, c = models.Section.objects.get_or_create( - name=section.getText().strip(), - journal=journal) - journal_models.SectionOrdering.objects.create(issue=issue, - section=order_section, - order=section_order).save() + name=section.getText().strip(), journal=journal + ) + journal_models.SectionOrdering.objects.create( + issue=issue, section=order_section, order=section_order + ).save() - import_issue_articles(soup_issue, issue, user, base_url, import_missing, update) + import_issue_articles( + soup_issue, issue, user, base_url, import_missing, update + ) issue.save() @@ -505,8 +558,10 @@ def import_jms_user(url, journal, auth_file, base_url, user_id): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # Fetch the user profile page and parse its metdata - resp, mime = utils_models.ImportCacheEntry.fetch(url=url, up_auth_file=auth_file, up_base_url=base_url) - soup_user_profile = BeautifulSoup(resp, 'lxml') + resp, mime = utils_models.ImportCacheEntry.fetch( + url=url, up_auth_file=auth_file, up_base_url=base_url + ) + soup_user_profile = BeautifulSoup(resp, "lxml") profile_dict = shared.get_user_profile(soup_user_profile)[0] if profile_dict["email"] == "journal@openlibhums.org": dummy_email = shared.generate_dummy_email(profile_dict) @@ -514,65 +569,68 @@ def import_jms_user(url, journal, auth_file, base_url, user_id): profile_dict["email"] = dummy_email # add an account for this new user - account = core_models.Account.objects.filter(email=profile_dict['email']) + account = core_models.Account.objects.filter(email=profile_dict["email"]) if account is not None and len(account) > 0: account = account[0] - logger.info("Found account for {0}".format(profile_dict['email'])) + logger.info("Found account for {0}".format(profile_dict["email"])) else: - logger.info("Didn't find account for {0}. Creating.".format(profile_dict['email'])) + logger.info( + "Didn't find account for {0}. Creating.".format(profile_dict["email"]) + ) - if profile_dict['Country'] == '—': - profile_dict['Country'] = None + if profile_dict["Country"] == "—": + profile_dict["Country"] = None else: try: - profile_dict['Country'] = core_models.Country.objects.get(name=profile_dict['Country']) + profile_dict["Country"] = core_models.Country.objects.get( + name=profile_dict["Country"] + ) except Exception: - logger.warning( - "Country not found: %s" % profile_dict["Country"]) - profile_dict['Country'] = None + logger.warning("Country not found: %s" % profile_dict["Country"]) + profile_dict["Country"] = None - if not profile_dict.get('Salutation') in dict(core_models.SALUTATION_CHOICES): - profile_dict['Salutation'] = '' + if not profile_dict.get("Salutation") in dict(core_models.SALUTATION_CHOICES): + profile_dict["Salutation"] = "" - if profile_dict.get('Middle Name', None) == '—': - profile_dict['Middle Name'] = '' + if profile_dict.get("Middle Name", None) == "—": + profile_dict["Middle Name"] = "" account = core_models.Account.objects.create( - email=profile_dict['email'], - username=profile_dict['Username'], - institution=profile_dict['Affiliation'], - first_name=profile_dict['First Name'], - last_name=profile_dict['Last Name'], - middle_name=profile_dict.get('Middle Name', ''), - country=profile_dict.get('Country', None), - biography=profile_dict.get('Bio Statement', ''), - salutation=profile_dict.get('Salutation', ''), + email=profile_dict["email"], + username=profile_dict["Username"], + institution=profile_dict["Affiliation"], + first_name=profile_dict["First Name"], + last_name=profile_dict["Last Name"], + middle_name=profile_dict.get("Middle Name", ""), + country=profile_dict.get("Country", None), + biography=profile_dict.get("Bio Statement", ""), + salutation=profile_dict.get("Salutation", ""), is_active=True, ) account.save() if account: - account.add_account_role(journal=journal, role_slug='author') - account.add_account_role(journal=journal, role_slug='reviewer') + account.add_account_role(journal=journal, role_slug="author") + account.add_account_role(journal=journal, role_slug="reviewer") def process_resp(resp): resp = resp.decode("utf-8") known_strings = { - '\\u00a0': " ", - '\\u00e0': "à", - '\\u0085': "...", - '\\u0091': "'", - '\\u0092': "'", - '\\u0093': '\\"', - '\\u0094': '\\"', - '\\u0096': "-", - '\\u0097': "-", - '\\u00F6': 'ö', - '\\u009a': 'š', - '\\u00FC': 'ü', + "\\u00a0": " ", + "\\u00e0": "à", + "\\u0085": "...", + "\\u0091": "'", + "\\u0092": "'", + "\\u0093": '\\"', + "\\u0094": '\\"', + "\\u0096": "-", + "\\u0097": "-", + "\\u00F6": "ö", + "\\u009a": "š", + "\\u00FC": "ü", } for string, replacement in known_strings.items(): @@ -581,47 +639,61 @@ def process_resp(resp): def get_input_value_by_name(content, name): - soup = BeautifulSoup(content, 'lxml') - value = soup.find('input', {'name': name}).get('value', None) + soup = BeautifulSoup(content, "lxml") + value = soup.find("input", {"name": name}).get("value", None) return value def convert_values(value): - return re.sub(r'[\xc2-\xf4][\x80-\xbf]+', lambda m: m.group(0).encode('latin1').decode('utf-8'), value) + return re.sub( + r"[\xc2-\xf4][\x80-\xbf]+", + lambda m: m.group(0).encode("latin1").decode("utf-8"), + value, + ) def get_ojs_plugin_response(url, auth_file, up_base_url): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # setup auth variables do_auth = True - username = '' - password = '' + username = "" + password = "" headers = { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/39.0.2171.95 Safari/537.36'} + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 Safari/537.36" + } session = requests.Session() # first, check whether there's an auth file - with open(auth_file, 'r', encoding="utf-8") as auth_in: + with open(auth_file, "r", encoding="utf-8") as auth_in: auth_dict = json.loads(auth_in.read()) do_auth = True - username = auth_dict['username'] - password = auth_dict['password'] + username = auth_dict["username"] + password = auth_dict["password"] # load the login page - auth_url = '{0}{1}'.format(up_base_url, '/login/') + auth_url = "{0}{1}".format(up_base_url, "/login/") fetched = session.get(auth_url, headers=headers, stream=True, verify=False) - csrf_token = get_input_value_by_name(fetched.content, 'csrfmiddlewaretoken') + csrf_token = get_input_value_by_name(fetched.content, "csrfmiddlewaretoken") - post_dict = {'username': username, 'password': password, 'login': 'login', 'csrfmiddlewaretoken': csrf_token} - fetched = session.post('{0}{1}'.format(up_base_url, '/author/login/'), data=post_dict, - headers={'Referer': auth_url, - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/39.0.2171.95 Safari/537.36' - }) + post_dict = { + "username": username, + "password": password, + "login": "login", + "csrfmiddlewaretoken": csrf_token, + } + fetched = session.post( + "{0}{1}".format(up_base_url, "/author/login/"), + data=post_dict, + headers={ + "Referer": auth_url, + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 Safari/537.36", + }, + ) fetched = session.get(url, headers=headers, stream=True, verify=False) @@ -639,27 +711,33 @@ def ojs_plugin_import_review_articles(url, journal, auth_file, base_url): for article_dict in resp: create_article_with_review_content(article_dict, journal, auth_file, base_url) - logger.info('Importing {article}.'.format(article=article_dict.get('title'))) + logger.info("Importing {article}.".format(article=article_dict.get("title"))) def ojs_plugin_import_editing_articles(url, journal, auth_file, base_url): resp = get_ojs_plugin_response(url, auth_file, base_url) for article_dict in resp: - logger.info('Importing {article}.'.format(article=article_dict.get('title'))) - article = create_article_with_review_content(article_dict, journal, auth_file, base_url) - complete_article_with_production_content(article, article_dict, journal, auth_file, base_url) + logger.info("Importing {article}.".format(article=article_dict.get("title"))) + article = create_article_with_review_content( + article_dict, journal, auth_file, base_url + ) + complete_article_with_production_content( + article, article_dict, journal, auth_file, base_url + ) def create_article_with_review_content(article_dict, journal, auth_file, base_url): - date_started = timezone.make_aware(dateparser.parse(article_dict.get('date_submitted'))) + date_started = timezone.make_aware( + dateparser.parse(article_dict.get("date_submitted")) + ) # Create a base article article = models.Article( journal=journal, - title=article_dict.get('title'), - abstract=article_dict.get('abstract'), - language=article_dict.get('language'), + title=article_dict.get("title"), + abstract=article_dict.get("abstract"), + language=article_dict.get("language"), stage=models.STAGE_UNDER_REVIEW, is_import=True, date_submitted=date_started, @@ -668,69 +746,73 @@ def create_article_with_review_content(article_dict, journal, auth_file, base_ur article.save() # Check for editors and assign them as section editors. - editors = article_dict.get('editors', []) + editors = article_dict.get("editors", []) for editor in editors: try: account = core_models.Account.objects.get(email=editor) - account.add_account_role('section-editor', journal) - review_models.EditorAssignment.objects.create(article=article, editor=account, editor_type='section-editor') - logger.info('Editor added to article') + account.add_account_role("section-editor", journal) + review_models.EditorAssignment.objects.create( + article=article, editor=account, editor_type="section-editor" + ) + logger.info("Editor added to article") except Exception as e: - logger.error('Editor account was not found.') + logger.error("Editor account was not found.") logger.exception(e) # Add a new review round round = review_models.ReviewRound.objects.create(article=article, round_number=1) # Add keywords - keywords = article_dict.get('keywords') + keywords = article_dict.get("keywords") if keywords: - for i, keyword in enumerate(keywords.split(';')): + for i, keyword in enumerate(keywords.split(";")): keyword = strip_tags(keyword) word, created = models.Keyword.objects.get_or_create(word=keyword) models.KeywordArticle.objects.update_or_create( keyword=keyword, article=article, - defaults={"order":i}, + defaults={"order": i}, ) # Add authors - for author in article_dict.get('authors'): + for author in article_dict.get("authors"): try: - author_record = core_models.Account.objects.get(email=author.get('email')) + author_record = core_models.Account.objects.get(email=author.get("email")) except core_models.Account.DoesNotExist: author_record = core_models.Account.objects.create( - email=author.get('email'), - first_name=author.get('first_name', ''), - last_name=author.get('last_name', ''), - institution=author.get('affiliation', ''), - biography=author.get('bio', ''), + email=author.get("email"), + first_name=author.get("first_name", ""), + last_name=author.get("last_name", ""), + institution=author.get("affiliation", ""), + biography=author.get("bio", ""), ) # If we have a country, fetch its record - if author.get('country'): + if author.get("country"): try: - country = core_models.Country.objects.get(code=author.get('country')) + country = core_models.Country.objects.get(code=author.get("country")) author_record.country = country author_record.save() except core_models.Country.DoesNotExist: pass # Add authors to m2m and create an order record article.authors.add(author_record) - models.ArticleAuthorOrder.objects.create(article=article, - author=author_record, - order=article.next_author_sort()) + models.ArticleAuthorOrder.objects.create( + article=article, author=author_record, order=article.next_author_sort() + ) # Set the primary author - article.owner = core_models.Account.objects.get(email=article_dict.get('correspondence_author')) + article.owner = core_models.Account.objects.get( + email=article_dict.get("correspondence_author") + ) article.correspondence_author = article.owner # Get or create the article's section try: section = models.Section.objects.get( journal=journal, - name=article_dict.get('section'), + name=article_dict.get("section"), ) except models.Section.DoesNotExist: section = None @@ -740,10 +822,9 @@ def create_article_with_review_content(article_dict, journal, auth_file, base_ur article.save() # Attempt to get the default review form - form = setting_handler.get_setting('general', - 'default_review_form', - journal, - create=True).processed_value + form = setting_handler.get_setting( + "general", "default_review_form", journal, create=True + ).processed_value if not form: try: @@ -751,32 +832,40 @@ def create_article_with_review_content(article_dict, journal, auth_file, base_ur except Exception: form = None logger.error( - 'You must have at least one review form for the journal before' - ' importing.' + "You must have at least one review form for the journal before" + " importing." ) exit() - for review in article_dict.get('reviews'): + for review in article_dict.get("reviews"): try: - reviewer = core_models.Account.objects.get(email=review.get('email')) + reviewer = core_models.Account.objects.get(email=review.get("email")) except core_models.Account.DoesNotExist: reviewer = core_models.Account.objects.create( - email=review.get('email'), - first_name=review.get('first_name'), - last_name=review.get('last_name'), + email=review.get("email"), + first_name=review.get("first_name"), + last_name=review.get("last_name"), ) # Parse the dates - date_requested = timezone.make_aware(dateparser.parse(review.get('date_requested'))) - date_due = timezone.make_aware(dateparser.parse(review.get('date_due'))) - date_complete = timezone.make_aware(dateparser.parse(review.get('date_complete'))) if review.get( - 'date_complete') else None - date_confirmed = timezone.make_aware(dateparser.parse(review.get('date_confirmed'))) if review.get( - 'date_confirmed') else None + date_requested = timezone.make_aware( + dateparser.parse(review.get("date_requested")) + ) + date_due = timezone.make_aware(dateparser.parse(review.get("date_due"))) + date_complete = ( + timezone.make_aware(dateparser.parse(review.get("date_complete"))) + if review.get("date_complete") + else None + ) + date_confirmed = ( + timezone.make_aware(dateparser.parse(review.get("date_confirmed"))) + if review.get("date_confirmed") + else None + ) # If the review was declined, setup a date declined date stamp - review.get('declined') - if review.get('declined') == '1': + review.get("declined") + if review.get("declined") == "1": date_declined = date_confirmed date_accepted = None date_complete = date_confirmed @@ -788,56 +877,79 @@ def create_article_with_review_content(article_dict, journal, auth_file, base_ur article=article, reviewer=reviewer, review_round=round, - review_type='traditional', - visibility='double-blind', + review_type="traditional", + visibility="double-blind", date_due=date_due, date_requested=date_requested, date_complete=date_complete, date_accepted=date_accepted, access_code=uuid.uuid4(), - form=form + form=form, ) - if review.get('declined') or review.get('recommendation'): + if review.get("declined") or review.get("recommendation"): new_review.is_complete = True - if review.get('recommendation'): - new_review.decision = map_review_recommendation(review.get('recommendation')) + if review.get("recommendation"): + new_review.decision = map_review_recommendation( + review.get("recommendation") + ) - if review.get('review_file_url'): - filename, mime = shared.fetch_file(base_url, review.get('review_file_url'), None, None, article, None, - handle_images=False, auth_file=auth_file) + if review.get("review_file_url"): + filename, mime = shared.fetch_file( + base_url, + review.get("review_file_url"), + None, + None, + article, + None, + handle_images=False, + auth_file=auth_file, + ) extension = os.path.splitext(filename)[1] - review_file = shared.add_file(mime, extension, 'Reviewer file', reviewer, filename, article, - galley=False) + review_file = shared.add_file( + mime, + extension, + "Reviewer file", + reviewer, + filename, + article, + galley=False, + ) new_review.review_file = review_file - if review.get('comments'): - filepath = core_files.create_temp_file(review.get('comments'), 'comment.txt') - file = open(filepath, 'r', encoding="utf-8") - comment_file = core_files.save_file_to_article(file, - article, - article.owner, - label='Review Comments', - save=False) + if review.get("comments"): + filepath = core_files.create_temp_file( + review.get("comments"), "comment.txt" + ) + file = open(filepath, "r", encoding="utf-8") + comment_file = core_files.save_file_to_article( + file, article, article.owner, label="Review Comments", save=False + ) new_review.review_file = comment_file new_review.save() # Get MS File - ms_file = get_ojs_file(base_url, article_dict.get('manuscript_file_url'), article, auth_file, 'MS File') + ms_file = get_ojs_file( + base_url, article_dict.get("manuscript_file_url"), article, auth_file, "MS File" + ) article.manuscript_files.add(ms_file) # Get RV File - rv_file = get_ojs_file(base_url, article_dict.get('review_file_url'), article, auth_file, 'RV File') + rv_file = get_ojs_file( + base_url, article_dict.get("review_file_url"), article, auth_file, "RV File" + ) round.review_files.add(rv_file) # Get Supp Files - if article_dict.get('supp_files'): - for file in article_dict.get('supp_files'): - file = get_ojs_file(base_url, file.get('url'), article, auth_file, file.get('title')) + if article_dict.get("supp_files"): + for file in article_dict.get("supp_files"): + file = get_ojs_file( + base_url, file.get("url"), article, auth_file, file.get("title") + ) article.data_figure_files.add(file) article.save() @@ -847,10 +959,20 @@ def create_article_with_review_content(article_dict, journal, auth_file, base_ur def get_ojs_file(base_url, url, article, auth_file, label): - filename, mime = shared.fetch_file(base_url, url, None, None, article, None, handle_images=False, - auth_file=auth_file) + filename, mime = shared.fetch_file( + base_url, + url, + None, + None, + article, + None, + handle_images=False, + auth_file=auth_file, + ) extension = os.path.splitext(filename)[1] - file = shared.add_file(mime, extension, label, article.owner, filename, article, galley=False) + file = shared.add_file( + mime, extension, label, article.owner, filename, article, galley=False + ) return file @@ -858,9 +980,18 @@ def get_ojs_file(base_url, url, article, auth_file, label): def determine_production_stage(article_dict): stage = models.STAGE_AUTHOR_COPYEDITING - publication = True if article_dict.get('publication') and article_dict['publication'].get('date_published') else False - typesetting = True if article_dict.get('layout') and article_dict['layout'].get('galleys') else False - proofing = True if typesetting and article_dict.get('proofing') else False + publication = ( + True + if article_dict.get("publication") + and article_dict["publication"].get("date_published") + else False + ) + typesetting = ( + True + if article_dict.get("layout") and article_dict["layout"].get("galleys") + else False + ) + proofing = True if typesetting and article_dict.get("proofing") else False logger.debug(typesetting, proofing, publication) @@ -883,29 +1014,32 @@ def attempt_to_make_timezone_aware(datetime): def import_copyeditors(article, article_dict, auth_file, base_url): - copyediting = article_dict.get('copyediting', None) + copyediting = article_dict.get("copyediting", None) if copyediting: - - initial = copyediting.get('initial') - author = copyediting.get('author') - final = copyediting.get('final') + initial = copyediting.get("initial") + author = copyediting.get("author") + final = copyediting.get("final") from copyediting import models if initial: - initial_copyeditor = core_models.Account.objects.get(email=initial.get('email')) - initial_decision = True if (initial.get('underway') or initial.get('complete')) else False + initial_copyeditor = core_models.Account.objects.get( + email=initial.get("email") + ) + initial_decision = ( + True if (initial.get("underway") or initial.get("complete")) else False + ) logger.info( - 'Adding copyeditor: {copyeditor}'.format( + "Adding copyeditor: {copyeditor}".format( copyeditor=initial_copyeditor.full_name() ) ) - assigned = attempt_to_make_timezone_aware(initial.get('notified')) - underway = attempt_to_make_timezone_aware(initial.get('underway')) - complete = attempt_to_make_timezone_aware(initial.get('complete')) + assigned = attempt_to_make_timezone_aware(initial.get("notified")) + underway = attempt_to_make_timezone_aware(initial.get("underway")) + complete = attempt_to_make_timezone_aware(initial.get("complete")) copyedit_assignment = models.CopyeditAssignment.objects.create( article=article, @@ -915,37 +1049,45 @@ def import_copyeditors(article, article_dict, auth_file, base_url): decision=initial_decision, date_decided=underway if underway else complete, copyeditor_completed=complete, - copyedit_accepted=complete + copyedit_accepted=complete, ) - if initial.get('file'): - file = get_ojs_file(base_url, initial.get('file'), article, auth_file, 'Copyedited File') + if initial.get("file"): + file = get_ojs_file( + base_url, initial.get("file"), article, auth_file, "Copyedited File" + ) copyedit_assignment.copyeditor_files.add(file) - if initial and author.get('notified'): - logger.info('Adding author review.') - assigned = attempt_to_make_timezone_aware(author.get('notified')) - complete = attempt_to_make_timezone_aware(author.get('complete')) + if initial and author.get("notified"): + logger.info("Adding author review.") + assigned = attempt_to_make_timezone_aware(author.get("notified")) + complete = attempt_to_make_timezone_aware(author.get("complete")) author_review = models.AuthorReview.objects.create( author=article.owner, assignment=copyedit_assignment, assigned=assigned, notified=True, - decision='accept', + decision="accept", date_decided=complete, ) - if author.get('file'): - file = get_ojs_file(base_url, author.get('file'), article, auth_file, 'Author Review File') + if author.get("file"): + file = get_ojs_file( + base_url, + author.get("file"), + article, + auth_file, + "Author Review File", + ) author_review.files_updated.add(file) - if final and initial_copyeditor and final.get('notified'): - logger.info('Adding final copyedit assignment.') + if final and initial_copyeditor and final.get("notified"): + logger.info("Adding final copyedit assignment.") - assigned = attempt_to_make_timezone_aware(initial.get('notified')) - underway = attempt_to_make_timezone_aware(initial.get('underway')) - complete = attempt_to_make_timezone_aware(initial.get('complete')) + assigned = attempt_to_make_timezone_aware(initial.get("notified")) + underway = attempt_to_make_timezone_aware(initial.get("underway")) + complete = attempt_to_make_timezone_aware(initial.get("complete")) final_decision = True if underway or complete else False @@ -960,31 +1102,31 @@ def import_copyeditors(article, article_dict, auth_file, base_url): copyedit_accepted=complete, ) - if final.get('file'): - file = get_ojs_file(base_url, final.get('file'), article, auth_file, 'Final File') + if final.get("file"): + file = get_ojs_file( + base_url, final.get("file"), article, auth_file, "Final File" + ) final_assignment.copyeditor_files.add(file) def import_typesetters(article, article_dict, auth_file, base_url): - layout = article_dict.get('layout') + layout = article_dict.get("layout") task = None - if layout.get('email'): - typesetter = core_models.Account.objects.get(email=layout.get('email')) + if layout.get("email"): + typesetter = core_models.Account.objects.get(email=layout.get("email")) - logger.info('Adding typesetter {name}'.format(name=typesetter.full_name())) + logger.info("Adding typesetter {name}".format(name=typesetter.full_name())) from production import models as production_models assignment = production_models.ProductionAssignment.objects.create( - article=article, - assigned=timezone.now(), - notified=True + article=article, assigned=timezone.now(), notified=True ) - assigned = attempt_to_make_timezone_aware(layout.get('notified')) - accepted = attempt_to_make_timezone_aware(layout.get('underway')) - complete = attempt_to_make_timezone_aware(layout.get('complete')) + assigned = attempt_to_make_timezone_aware(layout.get("notified")) + accepted = attempt_to_make_timezone_aware(layout.get("underway")) + complete = attempt_to_make_timezone_aware(layout.get("complete")) task = production_models.TypesetTask.objects.create( assignment=assignment, @@ -1008,20 +1150,19 @@ def import_proofing(article, article_dict, auth_file, base_url): def import_galleys(article, layout_dict, auth_file, base_url): galleys = list() - if layout_dict.get('galleys'): - - for galley in layout_dict.get('galleys'): + if layout_dict.get("galleys"): + for galley in layout_dict.get("galleys"): logger.info( - 'Adding Galley with label {label}'.format( - label=galley.get('label') - ) + "Adding Galley with label {label}".format(label=galley.get("label")) + ) + file = get_ojs_file( + base_url, galley.get("file"), article, auth_file, galley.get("label") ) - file = get_ojs_file(base_url, galley.get('file'), article, auth_file, galley.get('label')) new_galley = core_models.Galley.objects.create( article=article, file=file, - label=galley.get('label'), + label=galley.get("label"), ) galleys.append(new_galley) @@ -1050,14 +1191,16 @@ def process_for_publication(article, article_dict, auth_file, base_url): # mark proofing complete -def complete_article_with_production_content(article, article_dict, journal, auth_file, base_url): +def complete_article_with_production_content( + article, article_dict, journal, auth_file, base_url +): """ Completes the import of journal article that are in editing """ article.stage = determine_production_stage(article_dict) article.save() - logger.debug('Stage: {stage}'.format(stage=article.stage)) + logger.debug("Stage: {stage}".format(stage=article.stage)) if article.stage == models.STAGE_READY_FOR_PUBLICATION: process_for_publication(article, article_dict, auth_file, base_url) @@ -1068,26 +1211,27 @@ def complete_article_with_production_content(article, article_dict, journal, aut def import_collections(journal, base_url, owner, update=False): - collections_url = base_url + '/collections/special' + collections_url = base_url + "/collections/special" resp, mime = utils_models.ImportCacheEntry.fetch(url=collections_url) - soup = BeautifulSoup(resp, 'html.parser') + soup = BeautifulSoup(resp, "html.parser") collections_div = soup.find("ul", attrs={"id": "special-collection-grid"}) if collections_div: collections = collections_div.find_all("li") for idx, collection_div in enumerate(collections): collection_link = collection_div.find( - "a", attrs={"class": "collection-image"}) + "a", attrs={"class": "collection-image"} + ) collection_path = shared.get_soup(collection_link, "href") coll_url = base_url + collection_path - collection, created = import_collection( - journal, coll_url, owner, update) + collection, created = import_collection(journal, coll_url, owner, update) collection.order = idx if created or update: try: desc_div = collection_div.find( - "div", attrs={"class": "collections-description"}) + "div", attrs={"class": "collections-description"} + ) description = desc_div.find("p").text collection.short_description = description.strip() except AttributeError: @@ -1098,17 +1242,16 @@ def import_collections(journal, base_url, owner, update=False): def import_collection(journal, url, owner, update=False): resp, mime = utils_models.ImportCacheEntry.fetch(url=url) - soup = BeautifulSoup(resp, 'html.parser') + soup = BeautifulSoup(resp, "html.parser") base_url = url.split("/collections/special/")[0] img_div = soup.find("div", attrs={"class": "collection-image"}) title = img_div.find("h1").text.strip() blurb_div = soup.find("div", attrs={"class": "main-body-block"}) date_launched = extract_date_launched(blurb_div.find_all("h5")) - blurb = ''.join(str(tag) for tag in blurb_div.contents) + blurb = "".join(str(tag) for tag in blurb_div.contents) - coll_type = journal_models.IssueType.objects.get( - journal=journal, code="collection") + coll_type = journal_models.IssueType.objects.get(journal=journal, code="collection") collection, c = journal_models.Issue.objects.get_or_create( issue_type=coll_type, journal=journal, @@ -1124,8 +1267,7 @@ def import_collection(journal, url, owner, update=False): collection.date = date_launched collection.save() articles_div = soup.find("div", attrs={"class": "featured-block"}) - import_issue_articles( - articles_div, collection, owner, base_url, update, update) + import_issue_articles(articles_div, collection, owner, base_url, update, update) return collection, c @@ -1139,13 +1281,17 @@ def import_collection_images(soup, collection, base_url): collection.save() -def import_issue_articles(soup, issue, user, base_url, import_missing=False, update=False): +def import_issue_articles( + soup, issue, user, base_url, import_missing=False, update=False +): journal = issue.journal - logger.info("Extracting article orders within issue...", ) + logger.info( + "Extracting article orders within issue...", + ) # delete existing order models for issue journal_models.ArticleOrdering.objects.filter(issue=issue).delete() - pattern = re.compile(r'\/articles\/(.+?)/(.+?)/') + pattern = re.compile(r"\/articles\/(.+?)/(.+?)/") articles = soup.find_all(href=pattern) article_order = 0 @@ -1161,27 +1307,34 @@ def import_issue_articles(soup, issue, user, base_url, import_missing=False, upd # get a proper article object article = models.Article.get_article( - journal, 'doi', '{0}/{1}'.format(prefix, doi)) + journal, "doi", "{0}/{1}".format(prefix, doi) + ) if not article and import_missing: - logger.info( - "Article %s not found, importing...", article_url) - article = import_article(journal,user, base_url + article_url) + logger.info("Article %s not found, importing...", article_url) + article = import_article(journal, user, base_url + article_url) if article and article not in processed: article = import_article( - journal,user, base_url + article_url, update=update) + journal, user, base_url + article_url, update=update + ) thumb_img = article_link.find("img") if thumb_img: thumb_path = thumb_img["src"] filename, mime = shared.fetch_file( base_url, - thumb_path, "", - 'graphic', - article, user, + thumb_path, + "", + "graphic", + article, + user, ) shared.add_file( - mime, 'graphic', 'Thumbnail', - user, filename, article, + mime, + "graphic", + "Thumbnail", + user, + filename, + article, thumbnail=True, ) @@ -1197,7 +1350,6 @@ def import_issue_articles(soup, issue, user, base_url, import_missing=False, upd obj.order = article_order obj.save() - article_order += 1 processed.append(article) @@ -1211,15 +1363,15 @@ def extract_date_launched(headers): raw_date = text.split("Collection launched: ")[-1] header.extract() return dateparser.parse(raw_date) - except(IndexError, ValueError): + except (IndexError, ValueError): logger.debug("Failed to parse collection date: %s", text) return None def split_affiliation(affiliation): - parts = [p.strip() for p in affiliation.split(',')] + parts = [p.strip() for p in affiliation.split(",")] country = None - institution = department = '' + institution = department = "" if len(parts) == 1: institution = parts[0] @@ -1239,24 +1391,24 @@ def split_affiliation(affiliation): def split_name(name): - parts = name.split(' ') - return parts[0], ' '.join(parts[1:]) + parts = name.split(" ") + return parts[0], " ".join(parts[1:]) def scrape_editorial_team(journal, base_url): logger.info("Scraping editorial team page") - editorial_team_path = '/about/editorialteam/' - page = requests.get('{}{}'.format(base_url, editorial_team_path)) + editorial_team_path = "/about/editorialteam/" + page = requests.get("{}{}".format(base_url, editorial_team_path)) - soup = BeautifulSoup(page.content, 'lxml') - main_block = soup.find('div', {'class': 'major-floating-block'}) - divs = main_block.find_all('div', {'class': 'col-md-12'}) + soup = BeautifulSoup(page.content, "lxml") + main_block = soup.find("div", {"class": "major-floating-block"}) + divs = main_block.find_all("div", {"class": "col-md-12"}) cached_group = group = None member_sequence = 0 for div in divs: # Try to grab the headers - header = div.find('h2') + header = div.find("h2") header_sequence = 0 if header: if group != cached_group: @@ -1266,34 +1418,38 @@ def scrape_editorial_team(journal, base_url): name=header.text.strip(), journal=journal, press=press_models.Press.objects.first(), - sequence=header_sequence + sequence=header_sequence, ) header_sequence = header_sequence + 1 else: # look for team group - member_divs = div.find_all('div', {'class': 'col-md-6'}) + member_divs = div.find_all("div", {"class": "col-md-6"}) for member_div in member_divs: - name = member_div.find('h6') + name = member_div.find("h6") print(name, member_sequence) if name and name.text.strip(): - affiliation = member_div.find('h5') - email_link = member_div.find('a', {'class': 'fa-envelope'}) - website = member_div.find('a', {'class': 'fa-globe'}) - department, institution, country = split_affiliation(affiliation.text) + affiliation = member_div.find("h5") + email_link = member_div.find("a", {"class": "fa-envelope"}) + website = member_div.find("a", {"class": "fa-globe"}) + department, institution, country = split_affiliation( + affiliation.text + ) first_name, last_name = split_name(name.text.strip()) profile_dict = { - 'first_name': first_name, - 'last_name': last_name, - 'department': department, - 'institution': institution, + "first_name": first_name, + "last_name": last_name, + "department": department, + "institution": institution, } if email_link: - email_search = re.search(r'[\w\.-]+@[\w\.-]+', email_link.get('href')) + email_search = re.search( + r"[\w\.-]+@[\w\.-]+", email_link.get("href") + ) email = email_search.group(0) else: email = generate_dummy_email(profile_dict) if website: - website_url = website.get('href') + website_url = website.get("href") profile_dict["website"] = website_url profile_dict["username"] = email profile_dict["country"] = country @@ -1317,10 +1473,11 @@ def scrape_editorial_team(journal, base_url): user=account, defaults=dict( sequence=member_sequence, - ) + ), ) member_sequence = member_sequence + 1 + def scrape_editorial_bio(member_div, account): learn_more = [a for a in member_div.find_all("a") if "Learn More" in a.text] if learn_more: @@ -1337,33 +1494,35 @@ def scrape_editorial_bio(member_div, account): account.enable_public_profile = True account.save() + # Checking links and rewrite the ones we know about # Remove the authorship link + def scrape_policies_page(journal, base_url): logger.info("Scraping editorial policies page") - policy_page_path = '/about/editorialpolicies/' - url = '{}{}'.format(base_url, policy_page_path) + policy_page_path = "/about/editorialpolicies/" + url = "{}{}".format(base_url, policy_page_path) page = requests.get(url) - soup = BeautifulSoup(page.content, 'lxml') - h2 = soup.find('h2', text=' Peer Review Process') + soup = BeautifulSoup(page.content, "lxml") + h2 = soup.find("h2", text=" Peer Review Process") - peer_review_text = '' + peer_review_text = "" for element in h2.next_siblings: - if element.name == 'p': + if element.name == "p": peer_review_text = peer_review_text + str(element) setting_handler.save_setting( - 'general', - 'peer_review_info', + "general", + "peer_review_info", journal, peer_review_text, ) content = scrape_page(url) create_cms_page( - 'editorial-policies', - 'Editorial Policies', + "editorial-policies", + "Editorial Policies", str(content), journal, ) @@ -1375,7 +1534,7 @@ def process_header_tags(content, tag): sections = {} for header in headers: - section_text = '' + section_text = "" for element in header.next_siblings: if element.name == tag: break @@ -1388,140 +1547,113 @@ def process_header_tags(content, tag): def scrape_submissions_page(journal, base_url): logger.info("Scraping policies page") - submission_path = '/about/submissions/' - page = requests.get('{}{}'.format(base_url, submission_path)) - soup = BeautifulSoup(page.content, 'lxml') - main_block = soup.find('div', {'class': 'featured-block'}) + submission_path = "/about/submissions/" + page = requests.get("{}{}".format(base_url, submission_path)) + soup = BeautifulSoup(page.content, "lxml") + main_block = soup.find("div", {"class": "featured-block"}) - sections = process_header_tags(main_block, 'h1') + sections = process_header_tags(main_block, "h1") - if sections.get('Author Guidelines'): + if sections.get("Author Guidelines"): create_cms_page( - 'author-guidelines', - 'Author Guidelines', - sections.get('Author Guidelines'), - journal + "author-guidelines", + "Author Guidelines", + sections.get("Author Guidelines"), + journal, ) - if sections.get('Submission Preparation Checklist'): + if sections.get("Submission Preparation Checklist"): setting_handler.save_setting( - 'general', - 'submission_checklist', + "general", + "submission_checklist", journal, - sections.get('Submission Preparation Checklist'), + sections.get("Submission Preparation Checklist"), ) - if sections.get('Copyright Notice'): + if sections.get("Copyright Notice"): setting_handler.save_setting( - 'general', - 'copyright_notice', - journal, - sections.get('Copyright Notice') + "general", "copyright_notice", journal, sections.get("Copyright Notice") ) - if sections.get('Publication Fees'): + if sections.get("Publication Fees"): setting_handler.save_setting( - 'general', - 'publication_fees', - journal, - sections.get('Publication Fees') + "general", "publication_fees", journal, sections.get("Publication Fees") ) -def scrape_page(page_url, block_to_find='featured-block'): +def scrape_page(page_url, block_to_find="featured-block"): page = requests.get(page_url) - soup = BeautifulSoup(page.content, 'lxml') - return str(soup.find('div', {'class': block_to_find})) + soup = BeautifulSoup(page.content, "lxml") + return str(soup.find("div", {"class": block_to_find})) def create_cms_page(url, name, content, journal): content_type = ContentType.objects.get_for_model(journal) defaults = { - 'display_name': name, - 'content': content, - 'is_markdown': False, + "display_name": name, + "content": content, + "is_markdown": False, } cms_models.Page.objects.get_or_create( - content_type=content_type, - object_id=journal.pk, - name=url, - defaults=defaults + content_type=content_type, object_id=journal.pk, name=url, defaults=defaults ) def scrape_research_integrity_page(journal, base_url): logger.info("Scraping research integrity page") - research_integrity_url = '{}/about/research-integrity/'.format(base_url) + research_integrity_url = "{}/about/research-integrity/".format(base_url) content = scrape_page( research_integrity_url, ) - soup = BeautifulSoup(content, 'lxml') + soup = BeautifulSoup(content, "lxml") content = soup - for div in soup.find_all('div', {'class': 'person-image'}): + for div in soup.find_all("div", {"class": "person-image"}): div.decompose() - soup.select_one('.main-color-text').decompose() - soup.find('style').decompose() + soup.select_one(".main-color-text").decompose() + soup.find("style").decompose() - for div in soup.find_all('div', {'style': 'border-top: 1px dashed #b3cfd4;'}): + for div in soup.find_all("div", {"style": "border-top: 1px dashed #b3cfd4;"}): div.decompose() soup.section.unwrap() soup.section.unwrap() - create_cms_page( - 'research-integrity', - 'Research Integrity', - str(content), - journal - ) + create_cms_page("research-integrity", "Research Integrity", str(content), journal) def scrape_about_page(journal, base_url): logger.info("Scraping about page") - about_url = '{}/about/'.format(base_url) - content = scrape_page(about_url, block_to_find='main-body-block') - soup = BeautifulSoup(content, 'lxml') - sections = process_header_tags(soup, 'h2') + about_url = "{}/about/".format(base_url) + content = scrape_page(about_url, block_to_find="main-body-block") + soup = BeautifulSoup(content, "lxml") + sections = process_header_tags(soup, "h2") - if sections.get('Focus and Scope'): + if sections.get("Focus and Scope"): setting_handler.save_setting( - 'general', - 'focus_and_scope', - journal, - sections.get('Focus and Scope') + "general", "focus_and_scope", journal, sections.get("Focus and Scope") ) - if sections.get('Publication Frequency'): + if sections.get("Publication Frequency"): setting_handler.save_setting( - 'general', - 'publication_cycle', + "general", + "publication_cycle", journal, - sections.get('Publication Frequency') + sections.get("Publication Frequency"), ) - create_cms_page( - 'about', - 'About', - str(content), - journal - ) + create_cms_page("about", "About", str(content), journal) def scrape_cms_page(journal, page_url, page_name): logger.info("Scraping CMS page: %s", page_url) - content = scrape_page(page_url, block_to_find='major-floating-block') + content = scrape_page(page_url, block_to_find="major-floating-block") path = urlparse(page_url).path page_path = os.path.basename(os.path.normpath(path)) if content and page_path: - create_cms_page( - page_path, - page_name, - str(content), - journal - ) + create_cms_page(page_path, page_name, str(content), journal) def generate_dummy_email(profile_dict): - seed = ''.join(str(val) for val in profile_dict.values()) + seed = "".join(str(val) for val in profile_dict.values()) hashed = hashlib.md5(str(seed).encode("utf-8")).hexdigest() return "{0}@{1}".format(hashed, settings.DUMMY_EMAIL_DOMAIN) diff --git a/src/utils/install.py b/src/utils/install.py index 0ced46e651..df363d70cb 100755 --- a/src/utils/install.py +++ b/src/utils/install.py @@ -17,36 +17,40 @@ from cms import models as cms_models -def update_settings(journal_object=None, management_command=False, - overwrite_with_defaults=False, - file_path='utils/install/journal_defaults.json'): - """ Updates or creates the settings for a journal from journal_defaults.json. +def update_settings( + journal_object=None, + management_command=False, + overwrite_with_defaults=False, + file_path="utils/install/journal_defaults.json", +): + """Updates or creates the settings for a journal from journal_defaults.json. :param journal_object: the journal object to update or None to set the default setting value :param management_command: whether to print output to the console :return: None """ - with codecs.open(os.path.join(settings.BASE_DIR, file_path), encoding='utf-8') as json_data: - + with codecs.open( + os.path.join(settings.BASE_DIR, file_path), encoding="utf-8" + ) as json_data: default_data = json.load(json_data) for item in default_data: setting_group, created = core_models.SettingGroup.objects.get_or_create( - name=item['group'].get('name'), + name=item["group"].get("name"), ) setting_defaults = { - 'types': item['setting'].get('type'), - 'pretty_name': item['setting'].get('pretty_name'), - 'description': item['setting'].get('description'), - 'is_translatable': item['setting'].get('is_translatable') + "types": item["setting"].get("type"), + "pretty_name": item["setting"].get("pretty_name"), + "description": item["setting"].get("description"), + "is_translatable": item["setting"].get("is_translatable"), } setting, created = core_models.Setting.objects.get_or_create( - name=item['setting'].get('name'), + name=item["setting"].get("name"), group=setting_group, - defaults=setting_defaults + defaults=setting_defaults, ) if not created: @@ -56,13 +60,12 @@ def update_settings(journal_object=None, management_command=False, setting.save() setting_value, created = core_models.SettingValue.objects.get_or_create( - journal=journal_object, - setting=setting + journal=journal_object, setting=setting ) if created or overwrite_with_defaults: - value = item['value'].get('default') - if setting.types == 'json' and isinstance(value, (list, dict)): + value = item["value"].get("default") + if setting.types == "json" and isinstance(value, (list, dict)): value = json.dumps(value) setting_value.value = value @@ -70,12 +73,12 @@ def update_settings(journal_object=None, management_command=False, # clear the many-to-many relationship, mainly for overwrite procedures setting.editable_by.clear() - role_list = item.get('editable_by', None) + role_list = item.get("editable_by", None) # if no role list is found, default to the status quo # where editors and journal-managers can edit a setting if not role_list: - role_list = ['editor', 'journal-manager'] + role_list = ["editor", "journal-manager"] roles = core_models.Role.objects.filter( slug__in=role_list, @@ -83,22 +86,24 @@ def update_settings(journal_object=None, management_command=False, setting.editable_by.add(*roles) if management_command: - print('Parsed setting {0}'.format(item['setting'].get('name'))) + print("Parsed setting {0}".format(item["setting"].get("name"))) -def load_permissions(file_path='utils/install/journal_defaults.json'): - with codecs.open(os.path.join(settings.BASE_DIR, file_path), encoding='utf-8') as json_data: +def load_permissions(file_path="utils/install/journal_defaults.json"): + with codecs.open( + os.path.join(settings.BASE_DIR, file_path), encoding="utf-8" + ) as json_data: default_data = json.load(json_data) for item in default_data: setting_group = core_models.SettingGroup.objects.filter( - name=item['group'].get('name'), + name=item["group"].get("name"), ).first() setting = core_models.Setting.objects.get( - name=item['setting'].get('name'), + name=item["setting"].get("name"), group=setting_group, ) - role_list = item.get('editable_by', None) + role_list = item.get("editable_by", None) if role_list: roles = core_models.Role.objects.filter( @@ -115,80 +120,78 @@ def update_emails(journal_object=None, management_command=False): :param management_command: Boolean :return: Nothing """ - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json'), encoding='utf-8') as json_data: - + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/journal_defaults.json"), + encoding="utf-8", + ) as json_data: default_data = json.load(json_data) for item in default_data: - group_name = item['group'].get('name') - - if group_name == 'email': + group_name = item["group"].get("name") + if group_name == "email": setting_defaults = { - 'types': item['setting'].get('type'), - 'pretty_name': item['setting'].get('pretty_name'), - 'description': item['setting'].get('description'), - 'is_translatable': item['setting'].get('is_translatable') + "types": item["setting"].get("type"), + "pretty_name": item["setting"].get("pretty_name"), + "description": item["setting"].get("description"), + "is_translatable": item["setting"].get("is_translatable"), } setting, created = core_models.Setting.objects.get_or_create( - name=item['setting'].get('name'), + name=item["setting"].get("name"), group__name=group_name, - defaults=setting_defaults + defaults=setting_defaults, ) setting_value, created = core_models.SettingValue.objects.get_or_create( - journal=journal_object, - setting=setting + journal=journal_object, setting=setting ) - setting_value.value = item['value'].get('default') + setting_value.value = item["value"].get("default") setting_value.save() if management_command: - print('{0} Updated'.format(setting.name)) + print("{0} Updated".format(setting.name)) def update_license(journal_object, management_command=False): - """ Updates or creates the settings for a journal from journal_defaults.json. + """Updates or creates the settings for a journal from journal_defaults.json. :param journal_object: the journal object to update :param management_command: whether to print output to the console :return: None """ - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/licence.json'), encoding='utf-8') as json_data: - + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/licence.json"), encoding="utf-8" + ) as json_data: default_data = json.load(json_data) for item in default_data: default_dict = { - 'name': item['fields'].get('name'), - 'url': item['fields'].get('url'), - 'text': item['fields'].get('text'), + "name": item["fields"].get("name"), + "url": item["fields"].get("url"), + "text": item["fields"].get("text"), } licence, created = submission_models.Licence.objects.get_or_create( journal=journal_object, - short_name=item['fields'].get('short_name'), - defaults=default_dict + short_name=item["fields"].get("short_name"), + defaults=default_dict, ) if management_command: - print('Parsed licence {0}'.format(item['fields'].get('short_name'))) + print("Parsed licence {0}".format(item["fields"].get("short_name"))) def setup_submission_items(journal, manage_command=False): with codecs.open( - os.path.join( - settings.BASE_DIR, - 'utils/install/submission_items.json' - ) + os.path.join(settings.BASE_DIR, "utils/install/submission_items.json") ) as json_data: submission_items = json.load(json_data) for i, setting in enumerate(submission_items): - if not setting.get('group') == 'special': + if not setting.get("group") == "special": setting_obj = core_models.Setting.objects.get( - group__name=setting.get('group'), - name=setting.get('name'), + group__name=setting.get("group"), + name=setting.get("name"), ) else: setting_obj = None @@ -199,28 +202,30 @@ def setup_submission_items(journal, manage_command=False): existing_setting=setting_obj, ) - setattr(obj, 'title_{}'.format(settings.LANGUAGE_CODE), setting.get('title')) + setattr( + obj, "title_{}".format(settings.LANGUAGE_CODE), setting.get("title") + ) obj.save() def update_xsl_files(journal_object=None, management_command=False): with codecs.open( - os.path.join( settings.BASE_DIR, 'utils/install/xsl_files.json'), - encoding='utf-8', + os.path.join(settings.BASE_DIR, "utils/install/xsl_files.json"), + encoding="utf-8", ) as json_data: - default_data = json.load(json_data) for item in default_data: file_path = os.path.join( - settings.BASE_DIR, 'transform/xsl/', item["fields"]["file"]) - with open(file_path, 'rb') as f: + settings.BASE_DIR, "transform/xsl/", item["fields"]["file"] + ) + with open(file_path, "rb") as f: xsl_file = ContentFile(f.read()) xsl_file.name = item["fields"]["file"] default_dict = { - 'file': xsl_file, - 'comments': item["fields"].get("commments"), + "file": xsl_file, + "comments": item["fields"].get("commments"), } xsl, created = core_models.XSLFile.objects.get_or_create( label=item["fields"]["label"] or settings.DEFAULT_XSL_FILE_LABEL, @@ -228,40 +233,39 @@ def update_xsl_files(journal_object=None, management_command=False): ) if management_command: - print('Parsed XSL {0}'.format(item['fields'].get('label'))) + print("Parsed XSL {0}".format(item["fields"].get("label"))) def update_issue_types(journal_object, management_command=False): - """ Updates or creates the default issue types for journal + """Updates or creates the default issue types for journal :param journal_object: the journal object to update :param management_command: whether or not to print output to the console :return: None """ with codecs.open( - os.path.join(settings.BASE_DIR, 'utils/install/issue_type.json'), - encoding='utf-8' + os.path.join(settings.BASE_DIR, "utils/install/issue_type.json"), + encoding="utf-8", ) as json_data: default_data = json.load(json_data) for item in default_data: default_dict = { - 'pretty_name': item['fields'].get('pretty_name', ''), - 'custom_plural': item['fields'].get('custom_plural', ''), + "pretty_name": item["fields"].get("pretty_name", ""), + "custom_plural": item["fields"].get("custom_plural", ""), } - issue_type, created = models.IssueType.objects\ - .get_or_create( + issue_type, created = models.IssueType.objects.get_or_create( journal=journal_object, - code=item['fields']['code'], - defaults=default_dict + code=item["fields"]["code"], + defaults=default_dict, ) if management_command: - print('Parsed {0}'.format(issue_type)) + print("Parsed {0}".format(issue_type)) def journal(name, code, base_url, delete): - """ Installs a journal into the system. + """Installs a journal into the system. :param name: the name of the new journal :param code: the journal's short codename @@ -274,16 +278,16 @@ def journal(name, code, base_url, delete): try: models.Journal.objects.get(code=code, domain=base_url).delete() except models.Journal.DoesNotExist: - print('Journal not found, nothing to delete') + print("Journal not found, nothing to delete") journal_object = models.Journal.objects.create(code=code, domain=base_url) update_settings(journal_object, management_command=True) - setting_handler.save_setting('general', 'journal_name', journal_object, name) + setting_handler.save_setting("general", "journal_name", journal_object, name) journal_object.setup_directory() def press(name, code, domain): - """ Install the press. Each Janeway instance can host one press with an indefinite number of journals. + """Install the press. Each Janeway instance can host one press with an indefinite number of journals. :param name: the name of the press :param code: the press's short codename diff --git a/src/utils/ithenticate.py b/src/utils/ithenticate.py index 5005964921..8e3fcfa069 100755 --- a/src/utils/ithenticate.py +++ b/src/utils/ithenticate.py @@ -10,8 +10,8 @@ def ithenticate_is_enabled(journal): - username = setting_handler.get_setting('crosscheck', 'username', journal).value - password = setting_handler.get_setting('crosscheck', 'password', journal).value + username = setting_handler.get_setting("crosscheck", "username", journal).value + password = setting_handler.get_setting("crosscheck", "password", journal).value if username and password: return True @@ -20,25 +20,25 @@ def ithenticate_is_enabled(journal): def build_server(journal): - username = setting_handler.get_setting('crosscheck', 'username', journal).value - password = setting_handler.get_setting('crosscheck', 'password', journal).value + username = setting_handler.get_setting("crosscheck", "username", journal).value + password = setting_handler.get_setting("crosscheck", "password", journal).value server = xmlrpclib.ServerProxy("https://api.ithenticate.com/rpc") login = server.login({"username": username, "password": password}) - assert(login['status'] == 200) - sid = login['sid'] + assert login["status"] == 200 + sid = login["sid"] - return {'server': server, 'sid': sid} + return {"server": server, "sid": sid} def find_folder(server, sid, journal): found_folder = None - folder_list_response = server.folder.list({'sid': sid}) - assert (folder_list_response['status'] == 200) + folder_list_response = server.folder.list({"sid": sid}) + assert folder_list_response["status"] == 200 - for folder in folder_list_response['folders']: - if folder['name'] == journal.name: - found_folder = folder['id'] + for folder in folder_list_response["folders"]: + if folder["name"] == journal.name: + found_folder = folder["id"] break if found_folder is None: @@ -49,18 +49,23 @@ def find_folder(server, sid, journal): def make_folder(server, sid, journal): # Get the Ubiquity Press Folder then add a new journal folder inside and then return the id. - folder_groups = server.group.list({'sid': sid}) + folder_groups = server.group.list({"sid": sid}) top_level_group = None - if folder_groups['status'] == 200: - for group in folder_groups['groups']: - if group['name'] == 'My Folders': - top_level_group = group['id'] + if folder_groups["status"] == 200: + for group in folder_groups["groups"]: + if group["name"] == "My Folders": + top_level_group = group["id"] break - new_folder = {'sid': sid, 'name': journal.name, 'folder_group': top_level_group, 'description': 'Journal Folder'} + new_folder = { + "sid": sid, + "name": journal.name, + "folder_group": top_level_group, + "description": "Journal Folder", + } folder_add = server.folder.add(new_folder) - return folder_add['id'] + return folder_add["id"] def send_to_ithenticate(article, file): @@ -68,40 +73,38 @@ def send_to_ithenticate(article, file): server = build_server(article.journal) # 2. Try to get this journal's folder - folder_id = find_folder(server['server'], server['sid'], article.journal) + folder_id = find_folder(server["server"], server["sid"], article.journal) # 2.1 If the journal doesn't have a folder, make one if not folder_id: - folder_id = make_folder(server['server'], server['sid'], article.journal) + folder_id = make_folder(server["server"], server["sid"], article.journal) # 3. Prepare the submission first_author = article.correspondence_author - open_file = codecs.open(file.self_article_path(), 'rb') + open_file = codecs.open(file.self_article_path(), "rb") data = xmlrpclib.Binary(bytes(open_file.read())) article_dict = { - 'title': article.title, - 'author_first': first_author.first_name, - 'author_last': first_author.last_name, - 'filename': os.path.basename(file.original_filename), - 'upload': data, + "title": article.title, + "author_first": first_author.first_name, + "author_last": first_author.last_name, + "filename": os.path.basename(file.original_filename), + "upload": data, } submission_dict = { - 'sid': server['sid'], - 'folder': folder_id, - 'uploads': [article_dict], - 'submit_to': 1, + "sid": server["sid"], + "folder": folder_id, + "uploads": [article_dict], + "submit_to": 1, } - submission = server['server'].document.add( - submission_dict - ) + submission = server["server"].document.add(submission_dict) - if submission['status'] == 200: - return submission['uploaded'][0].get('id') + if submission["status"] == 200: + return submission["uploaded"][0].get("id") else: return None @@ -110,11 +113,13 @@ def fetch_url(article): server = build_server(article.journal) try: - document = server['server'].document.get({'id': int(article.ithenticate_id), 'sid': server['sid']}) - part_id = document['documents'][0]['parts'][0].get('id') - report = server['server'].report.get({'id': int(part_id), 'sid': server['sid']}) + document = server["server"].document.get( + {"id": int(article.ithenticate_id), "sid": server["sid"]} + ) + part_id = document["documents"][0]["parts"][0].get("id") + report = server["server"].report.get({"id": int(part_id), "sid": server["sid"]}) - url = report['view_only_url'] + url = report["view_only_url"] except KeyError: url = None @@ -126,11 +131,13 @@ def fetch_percentage(journal, articles): for article in articles: if article.ithenticate_id: - document = server['server'].document.get({'id': int(article.ithenticate_id), 'sid': server['sid']}) + document = server["server"].document.get( + {"id": int(article.ithenticate_id), "sid": server["sid"]} + ) try: - part = document['documents'][0]['parts'][0] - if part['score']: - article.ithenticate_score = part['score'] + part = document["documents"][0]["parts"][0] + if part["score"]: + article.ithenticate_score = part["score"] article.save() except BaseException: pass diff --git a/src/utils/logger.py b/src/utils/logger.py index a371b21fd1..d0ea79cca2 100644 --- a/src/utils/logger.py +++ b/src/utils/logger.py @@ -1,12 +1,14 @@ """ Janeway logging utilities and main logger """ + import logging import threading class LogPrefix(object): - """ A logging prefix scoped for the current thread """ + """A logging prefix scoped for the current thread""" + _local = threading.local() @property @@ -55,7 +57,8 @@ def do_prefix(self, msg): class PrefixedLoggerAdapter(logging.LoggerAdapter): - """ Adds the current prefix to the log line""" + """Adds the current prefix to the log line""" + def process(self, msg, extra): return _prefix.do_prefix(msg), extra diff --git a/src/utils/logic.py b/src/utils/logic.py index 44093bc153..6e0e1834e2 100644 --- a/src/utils/logic.py +++ b/src/utils/logic.py @@ -24,39 +24,41 @@ def parse_mailgun_webhook(post): - message_id = post.get('Message-Id') - token = post.get('token') - timestamp = post.get('timestamp') - signature = post.get('signature') - mailgun_event = post.get('event') + message_id = post.get("Message-Id") + token = post.get("token") + timestamp = post.get("timestamp") + signature = post.get("signature") + mailgun_event = post.get("event") try: event = models.LogEntry.objects.get(message_id=message_id) except models.LogEntry.DoesNotExist: - return 'No log entry with that message ID found.' + return "No log entry with that message ID found." - if event and (mailgun_event == 'dropped' or mailgun_event == 'bounced'): - event.message_status = 'failed' + if event and (mailgun_event == "dropped" or mailgun_event == "bounced"): + event.message_status = "failed" event.save() send_bounce_notification_to_event_actor(event) - return 'Message dropped, actor notified.' - elif event and mailgun_event == 'delivered': - event.message_status = 'delivered' + return "Message dropped, actor notified." + elif event and mailgun_event == "delivered": + event.message_status = "delivered" event.save() - return 'Message marked as delivered.' + return "Message marked as delivered." def verify_webhook(token, timestamp, signature): - api_key = settings.MAILGUN_ACCESS_KEY.encode('utf-8') - timestamp = timestamp.encode('utf-8') - token = token.encode('utf-8') - signature = signature.encode('utf-8') + api_key = settings.MAILGUN_ACCESS_KEY.encode("utf-8") + timestamp = timestamp.encode("utf-8") + token = token.encode("utf-8") + signature = signature.encode("utf-8") - hmac_digest = hmac.new(key=api_key, - msg='{}{}'.format(timestamp, token).encode('utf-8'), - digestmod=hashlib.sha256).hexdigest() + hmac_digest = hmac.new( + key=api_key, + msg="{}{}".format(timestamp, token).encode("utf-8"), + digestmod=hashlib.sha256, + ).hexdigest() - return hmac.compare_digest(signature, hmac_digest.encode('utf-8')) + return hmac.compare_digest(signature, hmac_digest.encode("utf-8")) def send_bounce_notification_to_event_actor(event): @@ -101,27 +103,27 @@ def send_bounce_notification_to_event_actor(event): if request.site_type: # Setup log dict log_dict = { - 'level': 'Info', - 'action_text': 'Email Delivery Failed ', - 'types': 'Email Delivery', - 'target': target, + "level": "Info", + "action_text": "Email Delivery Failed ", + "types": "Email Delivery", + "target": target, } notify_helpers.send_email_with_body_from_setting_template( request=request, - template='bounced_email_notification', - subject='subject_bounced_email_notification', + template="bounced_email_notification", + subject="subject_bounced_email_notification", to=to, context={ - 'target': target, - 'event': event, + "target": target, + "event": event, }, log_dict=log_dict, ) def build_url_for_request(request=None, path="", query=None, fragment=""): - """ Builds a url from the base url relevant for the current request context + """Builds a url from the base url relevant for the current request context :request: An instance of django.http.HTTPRequest :path: A str indicating the path :query: A dictionary with any GET parameters @@ -145,7 +147,7 @@ def replace_netloc_port(netloc, new_port): def build_url(netloc, port=None, scheme=None, path="", query=None, fragment=""): - """ Builds a url given all its parts + """Builds a url given all its parts :netloc: string :port: int :scheme: string @@ -160,9 +162,9 @@ def build_url(netloc, port=None, scheme=None, path="", query=None, fragment=""): # Allow '/' to match Django template filter |urlencode default behavior. if query and isinstance(query, QueryDict): # Support multiple values for the same key - query = query.urlencode(safe='/') + query = query.urlencode(safe="/") elif query and isinstance(query, dict): - query = urlencode(query, safe='/') + query = urlencode(query, safe="/") if scheme is None: scheme = GlobalRequestMiddleware.get_current_request().scheme @@ -187,7 +189,7 @@ def get_current_request(): def get_janeway_version(): - """ Returns the installed version of janeway + """Returns the installed version of janeway :return: `string` version """ return str(janeway_version) @@ -201,7 +203,9 @@ def get_log_entries(object): ) -def generate_sitemap(file, press=None, journal=None, repository=None, issue=None, subject=None): +def generate_sitemap( + file, press=None, journal=None, repository=None, issue=None, subject=None +): """ Returns a rendered sitemap """ @@ -212,30 +216,30 @@ def generate_sitemap(file, press=None, journal=None, repository=None, issue=None is_remote=False, ) repos = repo_models.Repository.objects.all() - template = 'common/site_map_index.xml' + template = "common/site_map_index.xml" context = { - 'journals': journals, - 'repos': repos, + "journals": journals, + "repos": repos, } elif journal: - template = 'common/journal_sitemap.xml' + template = "common/journal_sitemap.xml" context = { - 'journal': journal, + "journal": journal, } elif repository: - template = 'common/repo_sitemap.xml', + template = ("common/repo_sitemap.xml",) context = { - 'repo': repository, + "repo": repository, } elif issue: - template = 'common/issue_sitemap.xml' + template = "common/issue_sitemap.xml" context = { - 'issue': issue, + "issue": issue, } elif subject: - template = 'common/subject_sitemap.xml' + template = "common/subject_sitemap.xml" context = { - 'subject': subject, + "subject": subject, } if template and context: @@ -245,14 +249,14 @@ def generate_sitemap(file, press=None, journal=None, repository=None, issue=None ) file.write(content) else: - return 'Must pass a press, journal, issue, repository or subject object.' + return "Must pass a press, journal, issue, repository or subject object." def get_sitemap_path(path_parts, file_name): path = os.path.join( settings.BASE_DIR, - 'files', - 'sitemaps', + "files", + "sitemaps", *path_parts, ) if not os.path.exists(path): @@ -268,9 +272,9 @@ def get_sitemap_path(path_parts, file_name): def write_journal_sitemap(journal): journal_file_path = get_sitemap_path( path_parts=[journal.code], - file_name='sitemap.xml', + file_name="sitemap.xml", ) - with open(journal_file_path, 'w') as file: + with open(journal_file_path, "w") as file: generate_sitemap(file, journal=journal) file.close() @@ -278,9 +282,9 @@ def write_journal_sitemap(journal): def write_issue_sitemap(issue): issue_file_path = get_sitemap_path( path_parts=[issue.journal.code], - file_name='{}_sitemap.xml'.format(issue.pk), + file_name="{}_sitemap.xml".format(issue.pk), ) - with open(issue_file_path, 'w') as file: + with open(issue_file_path, "w") as file: generate_sitemap(file, issue=issue) file.close() @@ -288,9 +292,9 @@ def write_issue_sitemap(issue): def write_repository_sitemap(repository): repo_file_path = get_sitemap_path( path_parts=[repository.code], - file_name='sitemap.xml', + file_name="sitemap.xml", ) - with open(repo_file_path, 'w') as file: + with open(repo_file_path, "w") as file: generate_sitemap(file, repository=repository) file.close() @@ -298,9 +302,9 @@ def write_repository_sitemap(repository): def write_subject_sitemap(subject): subject_file_path = get_sitemap_path( path_parts=[subject.repository.code], - file_name='{}_sitemap.xml'.format(subject.pk) + file_name="{}_sitemap.xml".format(subject.pk), ) - with open(subject_file_path, 'w') as file: + with open(subject_file_path, "w") as file: generate_sitemap(file, subject=subject) file.close() @@ -309,9 +313,9 @@ def write_press_sitemap(): press = press_models.Press.objects.all().first() press_sitemap_path = get_sitemap_path( path_parts=[], - file_name='sitemap.xml', + file_name="sitemap.xml", ) - with open(press_sitemap_path, 'w') as file: + with open(press_sitemap_path, "w") as file: generate_sitemap(file, press=press) file.close() @@ -329,10 +333,9 @@ def get_aware_datetime(unparsed_string, use_noon_if_no_time=True): from django.utils.timezone import is_aware, make_aware if use_noon_if_no_time and re.fullmatch( - '[0-9]{4}-[0-9]{2}-[0-9]{2}', - unparsed_string + "[0-9]{4}-[0-9]{2}-[0-9]{2}", unparsed_string ): - unparsed_string += ' 12:00' + unparsed_string += " 12:00" parsed_datetime = dateparser.parse(unparsed_string) @@ -341,6 +344,8 @@ def get_aware_datetime(unparsed_string, use_noon_if_no_time=True): else: return make_aware(parsed_datetime) + def get_janeway_patch_version(): from janeway import __version__ + return f"{__version__.major}.{__version__.minor}.{__version__.patch}" diff --git a/src/utils/management/base.py b/src/utils/management/base.py index bb0852a3df..e959ff36dd 100644 --- a/src/utils/management/base.py +++ b/src/utils/management/base.py @@ -13,7 +13,7 @@ class ProfiledCommand(BaseCommand): - """ A base class for commands that can be profiled with --profile + """A base class for commands that can be profiled with --profile When the --profile flag is present, the command execution will be wrapped inside a profiler run. At the end of the command, the results of the profiler are printed to stdout and written to a temporary file. The file @@ -21,7 +21,7 @@ class ProfiledCommand(BaseCommand): """ def add_arguments(self, parser): - parser.add_argument('--profile', action="store_true") + parser.add_argument("--profile", action="store_true") def execute(self, *args, **options): do_profile = options.pop("profile", False) @@ -30,7 +30,7 @@ def execute(self, *args, **options): profiler.enable() super().execute(*args, **options) profiler.disable() - s_io=io.StringIO() + s_io = io.StringIO() stats = pstats.Stats(profiler, stream=s_io).sort_stats("ncalls") stats.print_stats() @@ -39,7 +39,7 @@ def execute(self, *args, **options): command = os.path.basename(py_filename) stamp = time.time() filename = f"{command}-{stamp}.prof" - path = os.path.join(settings.BASE_DIR, 'files', 'temp', filename) + path = os.path.join(settings.BASE_DIR, "files", "temp", filename) profiler.dump_stats(path) print(f"Profiling written to: {path}") else: diff --git a/src/utils/management/commands/alter_domains.py b/src/utils/management/commands/alter_domains.py index 4cbfd9d7f9..89d4345857 100755 --- a/src/utils/management/commands/alter_domains.py +++ b/src/utils/management/commands/alter_domains.py @@ -12,12 +12,12 @@ class Command(BaseCommand): help = "Allows users to alter journal/press domains" def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) + parser.add_argument("journal_code", nargs="?", default=None) def handle(self, *args, **options): """Allows users to alter journal/press domains" @@ -28,40 +28,50 @@ def handle(self, *args, **options): """ journal = None press = press_models.Press.objects.all()[0] - journal_code = options.get('journal_code', None) + journal_code = options.get("journal_code", None) if journal_code: - if journal_code == 'press': + if journal_code == "press": journal = press else: try: journal = journal_models.Journal.objects.get(code=journal_code) except journal_models.Journal.DoesNotExist: - print('No journal with that code found.') + print("No journal with that code found.") if not journal_code: journals = journal_models.Journal.objects.all() while True: for journal in journals: - print('Code: {code} - {name}'.format(code=journal.code, name=journal.name)) + print( + "Code: {code} - {name}".format( + code=journal.code, name=journal.name + ) + ) - print('Code: press - {press_name}'.format(press_name=press.name)) - journal_code = input('Please select a journal to edit, enter the ID number: ') + print("Code: press - {press_name}".format(press_name=press.name)) + journal_code = input( + "Please select a journal to edit, enter the ID number: " + ) try: - if journal_code == 'press': + if journal_code == "press": journal = press break journal = journals.get(code=journal_code) break except journal_models.Journal.DoesNotExist: - print('No journal with that code found.') + print("No journal with that code found.") time.sleep(2) - print('Altering domain for {journal}, current domain: {domain}'.format(journal=journal.name, domain=journal.domain)) - new_domain = input('Enter the new domain you wish to set: ') + print( + "Altering domain for {journal}, current domain: {domain}".format( + journal=journal.name, domain=journal.domain + ) + ) + new_domain = input("Enter the new domain you wish to set: ") - print('Altering domain record...', end='') + print("Altering domain record...", end="") journal.domain = new_domain journal.save() - print('... [Ok]') + print("... [Ok]") diff --git a/src/utils/management/commands/backup.py b/src/utils/management/commands/backup.py index 0c666f617d..aaee61a811 100755 --- a/src/utils/management/commands/backup.py +++ b/src/utils/management/commands/backup.py @@ -27,8 +27,10 @@ def copy_file(source, destination): os.mkdir(destination_folder) print("Copying {0}".format(source)) - shutil.copy(os.path.join(settings.BASE_DIR, source), - os.path.join(settings.BASE_DIR, destination)) + shutil.copy( + os.path.join(settings.BASE_DIR, source), + os.path.join(settings.BASE_DIR, destination), + ) def copy_files(src_path, dest_path): @@ -43,7 +45,7 @@ def copy_files(src_path, dest_path): files = os.listdir(src_path) for file_name in files: - if not file_name == 'temp': + if not file_name == "temp": full_file_name = os.path.join(src_path, file_name) print("Copying {0}".format(full_file_name)) if os.path.isfile(full_file_name): @@ -56,24 +58,26 @@ def copy_files(src_path, dest_path): def mycb(so_far, total): - print('{0} kb transferred out of {1}'.format(so_far / 1024, total / 1024)) + print("{0} kb transferred out of {1}".format(so_far / 1024, total / 1024)) def handle_s3(tmp_path, start_time): print("Sending to S3.") - file_name = '{0}.zip'.format(start_time) - file_path = os.path.join(settings.BASE_DIR, 'files', 'temp', file_name) - f = open(file_path, 'rb') + file_name = "{0}.zip".format(start_time) + file_path = os.path.join(settings.BASE_DIR, "files", "temp", file_name) + f = open(file_path, "rb") END_POINT = settings.END_POINT S3_HOST = settings.S3_HOST - UPLOADED_FILENAME = 'backups/{0}.zip'.format(start_time) + UPLOADED_FILENAME = "backups/{0}.zip".format(start_time) # include folders in file path. If it doesn't exist, it will be created - s3 = boto.s3.connect_to_region(END_POINT, - aws_access_key_id=settings.S3_ACCESS_KEY, - aws_secret_access_key=settings.S3_SECRET_KEY, - host=S3_HOST) + s3 = boto.s3.connect_to_region( + END_POINT, + aws_access_key_id=settings.S3_ACCESS_KEY, + aws_secret_access_key=settings.S3_SECRET_KEY, + host=S3_HOST, + ) bucket = s3.get_bucket(settings.S3_BUCKET_NAME) k = Key(bucket) @@ -83,28 +87,30 @@ def handle_s3(tmp_path, start_time): def handle_directory(tmp_path, start_time): print("Copying to backup dir") - file_name = '{0}.zip'.format(start_time) - copy_file('files/temp/{0}'.format(file_name), settings.BACKUP_DIR) + file_name = "{0}.zip".format(start_time) + copy_file("files/temp/{0}".format(file_name), settings.BACKUP_DIR) def delete_used_tmp(tmp_path, start_time): print("Deleting temp directory.") shutil.rmtree(tmp_path) - file_path = "{0}/{1}.zip".format(os.path.join(settings.BASE_DIR, 'files', 'temp'), start_time) + file_path = "{0}/{1}.zip".format( + os.path.join(settings.BASE_DIR, "files", "temp"), start_time + ) os.unlink(file_path) def send_email(start_time, e, success=False): admins = models.Account.objects.filter(is_superuser=True) - message = '' + message = "" if not success: - message = 'There was an error during the backup process.\n\n ' + message = "There was an error during the backup process.\n\n " send_mail( - 'Backup', - '{0}{1}.'.format(message, e), - 'backup@janeway', + "Backup", + "{0}{1}.".format(message, e), + "backup@janeway", [user.email for user in admins], fail_silently=False, ) @@ -126,38 +132,58 @@ def handle(self, *args, **options): """ # Ensure temp dir exists: - if not os.path.exists(os.path.join(settings.BASE_DIR, 'files', 'temp')): - os.makedirs(os.path.join(settings.BASE_DIR, 'files', 'temp')) + if not os.path.exists(os.path.join(settings.BASE_DIR, "files", "temp")): + os.makedirs(os.path.join(settings.BASE_DIR, "files", "temp")) start_time = str(timezone.now()) try: - tmp_path = os.path.join(settings.BASE_DIR, 'files', 'temp', start_time) + tmp_path = os.path.join(settings.BASE_DIR, "files", "temp", start_time) # dump database out to JSON and store in StringIO for saving - print('Dumping json db file') + print("Dumping json db file") json_out = StringIO() - call_command('dumpdata', '--indent=4', '--natural-foreign', '--exclude=contenttypes', stdout=json_out) - - write_path = os.path.join(settings.BASE_DIR, 'files', 'temp', 'janeway.json') - with open(write_path, 'w', encoding="utf-8") as write: + call_command( + "dumpdata", + "--indent=4", + "--natural-foreign", + "--exclude=contenttypes", + stdout=json_out, + ) + + write_path = os.path.join( + settings.BASE_DIR, "files", "temp", "janeway.json" + ) + with open(write_path, "w", encoding="utf-8") as write: json_out.seek(0) shutil.copyfileobj(json_out, write) os.mkdir(tmp_path) - copy_file('files/temp/janeway.json', 'files/temp/{0}/janeway.json'.format(start_time)) - copy_files(os.path.join(settings.BASE_DIR, 'media'), os.path.join(tmp_path, 'media')) - copy_files(os.path.join(settings.BASE_DIR, 'files'), os.path.join(tmp_path, 'files')) + copy_file( + "files/temp/janeway.json", + "files/temp/{0}/janeway.json".format(start_time), + ) + copy_files( + os.path.join(settings.BASE_DIR, "media"), + os.path.join(tmp_path, "media"), + ) + copy_files( + os.path.join(settings.BASE_DIR, "files"), + os.path.join(tmp_path, "files"), + ) print("Creating archive.") - shutil.make_archive(os.path.join(settings.BASE_DIR, 'files', 'temp', start_time), 'zip', tmp_path) + shutil.make_archive( + os.path.join(settings.BASE_DIR, "files", "temp", start_time), + "zip", + tmp_path, + ) - if settings.BACKUP_TYPE == 's3': + if settings.BACKUP_TYPE == "s3": handle_s3(tmp_path, start_time) else: handle_directory(tmp_path, start_time) delete_used_tmp(tmp_path, start_time) if settings.BACKUP_EMAIL: - send_email(start_time, 'Backup was successfully completed.') + send_email(start_time, "Backup was successfully completed.") except Exception as e: - send_email(start_time, e) diff --git a/src/utils/management/commands/build_assets.py b/src/utils/management/commands/build_assets.py index 0188a3f386..12ef597d42 100755 --- a/src/utils/management/commands/build_assets.py +++ b/src/utils/management/commands/build_assets.py @@ -21,7 +21,6 @@ def handle(self, *args, **options): try: builder = import_module(module_name) except ModuleNotFoundError: - logger.info( - "Theme '%s' doesn't implement 'build_assets'.", theme) + logger.info("Theme '%s' doesn't implement 'build_assets'.", theme) else: builder.build() diff --git a/src/utils/management/commands/check_mailgun_stat.py b/src/utils/management/commands/check_mailgun_stat.py index 365ecfe01a..1a8f9bfe16 100755 --- a/src/utils/management/commands/check_mailgun_stat.py +++ b/src/utils/management/commands/check_mailgun_stat.py @@ -13,19 +13,19 @@ def get_logs(message_id): try: api_url = settings.MAILGUN_API_URL except AttributeError: - api_url = 'https://api.mailgun.net/v3/' + api_url = "https://api.mailgun.net/v3/" return requests.get( f"{api_url}{settings.MAILGUN_SERVER_NAME}/events", auth=("api", settings.MAILGUN_ACCESS_KEY), - params={"message-id": message_id}) + params={"message-id": message_id}, + ) def check_for_perm_failure(event_dict, log): - - for event in event_dict.get('items'): - severity = event.get('severity', None) - if severity == 'permanent': + for event in event_dict.get("items"): + severity = event.get("severity", None) + if severity == "permanent": return True return False @@ -42,57 +42,56 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--article-id', type=int) - parser.add_argument('--email-log-id', type=int) + parser.add_argument("--article-id", type=int) + parser.add_argument("--email-log-id", type=int) def handle(self, *args, **options): - if settings.ENABLE_ENHANCED_MAILGUN_FEATURES: - article_id = options.get('article_id') - email_log_id = options.get('email_log_id') + article_id = options.get("article_id") + email_log_id = options.get("email_log_id") email_logs = models.LogEntry.objects.filter( - is_email=True, - message_id__isnull=False, - status_checks_complete=False, + is_email=True, + message_id__isnull=False, + status_checks_complete=False, ) if article_id: article = submission_models.Article.objects.get(pk=article_id) content_type = ContentType.objects.get_for_model(article) email_logs = email_logs.filter( - content_type=content_type, - object_id=article.pk, + content_type=content_type, + object_id=article.pk, ) if email_log_id: email_logs.filter(pk=email_log_id) for log in email_logs: - logs = get_logs(log.message_id.replace('<', '').replace('>', '')) + logs = get_logs(log.message_id.replace("<", "").replace(">", "")) event_dict = logs.json() - print('Processing ', log.message_id, '...', end='') + print("Processing ", log.message_id, "...", end="") events = [] - for event in event_dict.get('items'): - events.append(event['event']) + for event in event_dict.get("items"): + events.append(event["event"]) - if 'delivered' in events: - log.message_status = 'delivered' + if "delivered" in events: + log.message_status = "delivered" log.status_checks_complete = True - elif 'failed' in events or 'bounced' in events: + elif "failed" in events or "bounced" in events: if check_for_perm_failure(event_dict, log): - log.message_status = 'failed' + log.message_status = "failed" log.status_checks_complete = True logic.send_bounce_notification_to_event_actor(log) else: - log.message_status = 'accepted' + log.message_status = "accepted" - elif 'accepted' in events: - log.message_status = 'accepted' + elif "accepted" in events: + log.message_status = "accepted" log.number_status_checks += 1 - print(' status {0}'.format(log.message_status)) + print(" status {0}".format(log.message_status)) log.save() else: - print('ENHANCED_MAILGUN_FEATURES is set to FALSE in settings.py') + print("ENHANCED_MAILGUN_FEATURES is set to FALSE in settings.py") diff --git a/src/utils/management/commands/check_settings_files.py b/src/utils/management/commands/check_settings_files.py index a5fd4e8a5d..728125a345 100755 --- a/src/utils/management/commands/check_settings_files.py +++ b/src/utils/management/commands/check_settings_files.py @@ -9,9 +9,12 @@ def update_default_setting(default_data, db_setting): for default_setting in default_data: - if default_setting['setting'].get('name') == db_setting.setting.name and default_setting['group'].get('name') == db_setting.setting.group.name: - print('Updating {0}'.format(db_setting.setting.name)) - default_setting['setting']['value'] = db_setting.value + if ( + default_setting["setting"].get("name") == db_setting.setting.name + and default_setting["group"].get("name") == db_setting.setting.group.name + ): + print("Updating {0}".format(db_setting.setting.name)) + default_setting["setting"]["value"] = db_setting.value class Command(BaseCommand): @@ -20,27 +23,35 @@ class Command(BaseCommand): help = "Synchronizes unspecified default settings to all journals." def handle(self, *args, **options): - translation.activate('en') + translation.activate("en") - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json'), 'r+', encoding='utf-8') as json_data: + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/journal_defaults.json"), + "r+", + encoding="utf-8", + ) as json_data: default_data = json.load(json_data) - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/test.json'), 'r+', encoding='utf-8') as test_json_data: + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/test.json"), + "r+", + encoding="utf-8", + ) as test_json_data: test_data = json.load(test_json_data) print(len(default_data)) print(len(test_data)) for setting in default_data: - setting_name = setting['setting']['name'] + setting_name = setting["setting"]["name"] found = False for test_setting in test_data: - test_setting_name = setting['setting']['name'] + test_setting_name = setting["setting"]["name"] if test_setting_name == setting_name: found = True if found: - print('{0} found'.format(setting_name)) + print("{0} found".format(setting_name)) else: - print('{0} not found'.format(setting_name)) + print("{0} not found".format(setting_name)) diff --git a/src/utils/management/commands/clear_cache.py b/src/utils/management/commands/clear_cache.py index c05b955d47..8e0a29052f 100644 --- a/src/utils/management/commands/clear_cache.py +++ b/src/utils/management/commands/clear_cache.py @@ -9,4 +9,4 @@ class Command(BaseCommand): help = "Clears the Django cache.." def handle(self, *args, **options): - shared.clear_cache() \ No newline at end of file + shared.clear_cache() diff --git a/src/utils/management/commands/copy_collection_cover_to_large_image.py b/src/utils/management/commands/copy_collection_cover_to_large_image.py index aefe05e18e..1733574e9c 100644 --- a/src/utils/management/commands/copy_collection_cover_to_large_image.py +++ b/src/utils/management/commands/copy_collection_cover_to_large_image.py @@ -12,19 +12,19 @@ class Command(BaseCommand): help = "Copies, for any given journals, collection cover images into the large image attribute." def add_arguments(self, parser): - parser.add_argument('--journal_codes', nargs='+') + parser.add_argument("--journal_codes", nargs="+") def handle(self, *args, **options): - journal_codes = options.get('journal_codes') + journal_codes = options.get("journal_codes") print(journal_codes) journals = models.Journal.objects.filter(code__in=journal_codes) for journal in journals: - print('Processing {}'.format(journal.name)) + print("Processing {}".format(journal.name)) collections = models.Issue.objects.filter( - issue_type__code='collection', + issue_type__code="collection", ) for collection in collections: - print('Processing {}'.format(collection)) + print("Processing {}".format(collection)) collection.large_image = collection.cover_image collection.save() diff --git a/src/utils/management/commands/disable_subtitle.py b/src/utils/management/commands/disable_subtitle.py index b1c254bf45..956e5d1773 100644 --- a/src/utils/management/commands/disable_subtitle.py +++ b/src/utils/management/commands/disable_subtitle.py @@ -10,6 +10,6 @@ class Command(BaseCommand): def handle(self, *args, **options): for journal in journal_models.Journal.objects.all(): - if hasattr(journal, 'submissionconfiguration'): + if hasattr(journal, "submissionconfiguration"): journal.submissionconfiguration.subtitle = False journal.submissionconfiguration.save() diff --git a/src/utils/management/commands/doi_check.py b/src/utils/management/commands/doi_check.py index 393e75a20d..0aa1f41eea 100755 --- a/src/utils/management/commands/doi_check.py +++ b/src/utils/management/commands/doi_check.py @@ -23,7 +23,7 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) + parser.add_argument("journal_code", nargs="?", default=None) def handle(self, *args, **options): """Runs through articles and checks their DOIs resolve. @@ -32,8 +32,10 @@ def handle(self, *args, **options): :param options: None :return: None """ - if options.get('journal_code'): - journals = journal_models.Journal.objects.filter(code=options.get('journal_code')) + if options.get("journal_code"): + journals = journal_models.Journal.objects.filter( + code=options.get("journal_code") + ) else: journals = journal_models.Journal.objects.all() request = cron_models.Request() @@ -41,34 +43,38 @@ def handle(self, *args, **options): for journal in journals: request.secure = journal.is_secure - print('Processing {0}'.format(journal.name)) + print("Processing {0}".format(journal.name)) articles = submission_models.Article.objects.filter(journal=journal) for article in articles: - doi = article.get_identifier('doi', object=True) + doi = article.get_identifier("doi", object=True) if doi and doi.is_doi: - print('Article {0} with DOI {1} processing.'.format(article.pk, doi)) + print( + "Article {0} with DOI {1} processing.".format(article.pk, doi) + ) should_resolve_to = article.url resolves_to = requests.get(doi.get_doi_url()) if not should_resolve_to == resolves_to.url: - print('Failure detected.') + print("Failure detected.") print(should_resolve_to, resolves_to.url) o, c = ident_models.BrokenDOI.objects.get_or_create( identifier=doi, article=article, - defaults={'checked': timezone.now(), - 'resolves_to': resolves_to.url, - 'expected_to_resolve_to': should_resolve_to} + defaults={ + "checked": timezone.now(), + "resolves_to": resolves_to.url, + "expected_to_resolve_to": should_resolve_to, + }, ) if c: - print('This failure is new \n') + print("This failure is new \n") else: - print('This failure has previously been detected \n') + print("This failure has previously been detected \n") else: try: ident_models.BrokenDOI.objects.get(identifier=doi).delete() @@ -76,4 +82,4 @@ def handle(self, *args, **options): pass else: - print('Article {0} has no DOI, skipping \n'.format(article.pk)) + print("Article {0} has no DOI, skipping \n".format(article.pk)) diff --git a/src/utils/management/commands/dump_file_text_to_db.py b/src/utils/management/commands/dump_file_text_to_db.py index 5417a8530b..bd32976fdf 100644 --- a/src/utils/management/commands/dump_file_text_to_db.py +++ b/src/utils/management/commands/dump_file_text_to_db.py @@ -13,10 +13,10 @@ class Command(BaseCommand): """ def add_arguments(self, parser): - parser.add_argument('--journal-code', type=str) - parser.add_argument('--article-id', type=int) - parser.add_argument('--file-id', type=int) - parser.add_argument('--all', action="store_true", default=False) + parser.add_argument("--journal-code", type=str) + parser.add_argument("--article-id", type=int) + parser.add_argument("--file-id", type=int) + parser.add_argument("--all", action="store_true", default=False) def handle(self, *args, **options): errors = [] diff --git a/src/utils/management/commands/generate_a_records.py b/src/utils/management/commands/generate_a_records.py index f53498ad9b..722e2ba5c3 100755 --- a/src/utils/management/commands/generate_a_records.py +++ b/src/utils/management/commands/generate_a_records.py @@ -6,6 +6,7 @@ from press.models import Press from core.models import DomainAlias + class Command(BaseCommand): """ A management command that prints a set of A records for your install @@ -14,19 +15,23 @@ class Command(BaseCommand): help = "Prints A records in plain text." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--serverip', default=False) + parser.add_argument("--serverip", default=False) def handle(self, *args, **options): sites = chain( - Journal.objects.all(), - Press.objects.all(), - DomainAlias.objects.all(), + Journal.objects.all(), + Press.objects.all(), + DomainAlias.objects.all(), ) for site in sites: - print('{domain}. IN A {ipaddress}'.format(domain=site.domain, ipaddress=options.get('serverip'))) + print( + "{domain}. IN A {ipaddress}".format( + domain=site.domain, ipaddress=options.get("serverip") + ) + ) diff --git a/src/utils/management/commands/generate_robots.py b/src/utils/management/commands/generate_robots.py index 12a9f39d77..8e488ff1cb 100644 --- a/src/utils/management/commands/generate_robots.py +++ b/src/utils/management/commands/generate_robots.py @@ -16,14 +16,13 @@ class Command(BaseCommand): help = "Generates robots.txt files." def handle(self, *args, **options): - site_map_path = reverse( - 'website_sitemap', + "website_sitemap", ) storage_path = os.path.join( settings.BASE_DIR, - 'files', - 'robots', + "files", + "robots", ) if not os.path.exists(storage_path): os.makedirs(storage_path) @@ -33,19 +32,16 @@ def handle(self, *args, **options): path=site_map_path, ) press_robots_txt = render_to_string( - 'common/robots.txt', - {'url': press_url}, + "common/robots.txt", + {"url": press_url}, ) - file_path = os.path.join( - storage_path, - 'robots.txt' - ) - with open(file_path, 'w+') as file: + file_path = os.path.join(storage_path, "robots.txt") + with open(file_path, "w+") as file: file.write(press_robots_txt) file.close() - if settings.URL_CONFIG == 'domain': + if settings.URL_CONFIG == "domain": domain_mode_journals = journal_models.Journal.objects.filter( domain__isnull=False, ) @@ -54,14 +50,12 @@ def handle(self, *args, **options): path=site_map_path, ) journal_robots_txt = render_to_string( - 'common/robots.txt', - {'url': journal_url} + "common/robots.txt", {"url": journal_url} ) file_path = os.path.join( - storage_path, - 'journal_{}_robots.txt'.format(journal.code) + storage_path, "journal_{}_robots.txt".format(journal.code) ) - with open(file_path, 'w') as file: + with open(file_path, "w") as file: file.write(journal_robots_txt) file.close() @@ -73,13 +67,11 @@ def handle(self, *args, **options): path=site_map_path, ) repo_robots_txt = render_to_string( - 'common/robots.txt', - {'url': repo_url} + "common/robots.txt", {"url": repo_url} ) file_path = os.path.join( - storage_path, - 'repo_{}_robots.txt'.format(repo.code) + storage_path, "repo_{}_robots.txt".format(repo.code) ) - with open(file_path, 'w') as file: + with open(file_path, "w") as file: file.write(repo_robots_txt) - file.close() \ No newline at end of file + file.close() diff --git a/src/utils/management/commands/generate_search_indexes.py b/src/utils/management/commands/generate_search_indexes.py index a253564353..e24cea4e5d 100644 --- a/src/utils/management/commands/generate_search_indexes.py +++ b/src/utils/management/commands/generate_search_indexes.py @@ -10,27 +10,27 @@ FT_SEARCH_TRANSLATABLE_COLUMNS = { - ('submission_article', 'title'), - ('submission_article', 'abstract'), + ("submission_article", "title"), + ("submission_article", "abstract"), } FT_SEARCH_COLUMNS = { - ('submission_frozenauthor', 'first_name'), - ('submission_frozenauthor', 'last_name'), - ('submission_keyword', 'word'), - ('core_filetext', 'contents'), + ("submission_frozenauthor", "first_name"), + ("submission_frozenauthor", "last_name"), + ("submission_keyword", "word"), + ("core_filetext", "contents"), } class Command(BaseCommand): - """ A management that generates search indexes""" + """A management that generates search indexes""" help = "Generates database indexes for full text search (idempotent)" INDEXING_SQL_TEMPLATES = { # We use GIN over GiST indexes as the former are more performant during - # Lookups at the expense of lower build/update performance + # Lookups at the expense of lower build/update performance # https://www.postgresql.org/docs/current/textsearch-indexes.html "postgresql": "CREATE INDEX {idx_name} ON {table} USING gin({col});", "mysql": "CREATE FULLTEXT INDEX {idx_name} on {table}({col});", @@ -38,7 +38,7 @@ class Command(BaseCommand): def handle(self, *args, **options): if not settings.ENABLE_FULL_TEXT_SEARCH: - logger.info('Full Text search not enabled') + logger.info("Full Text search not enabled") return cursor = connection.cursor() if connection.vendor in self.INDEXING_SQL_TEMPLATES: @@ -46,20 +46,18 @@ def handle(self, *args, **options): try: cursor.execute("CREATE EXTENSION btree_gin;") except ProgrammingError: - pass # Ignore if already exists + pass # Ignore if already exists for table, col in self.get_columns_to_index(connection.vendor): idx_name = f"{col}_ft_idx" sql = self.INDEXING_SQL_TEMPLATES[connection.vendor].format( - col=col, - table=table, - idx_name=idx_name + col=col, table=table, idx_name=idx_name ) logger.debug("running SQL: %s", sql) try: cursor.execute(sql) except (OperationalError, ProgrammingError) as e: - pass # Ignore if already exists + pass # Ignore if already exists else: logger.warning( "Full-text indexing on %s backend not supported", @@ -68,12 +66,10 @@ def handle(self, *args, **options): def get_columns_to_index(self, vendor): for table, col in FT_SEARCH_COLUMNS: - if table == "core_filetext" and vendor =="postgresql": + if table == "core_filetext" and vendor == "postgresql": continue yield table, col for table, col in FT_SEARCH_TRANSLATABLE_COLUMNS: for lang, _ in settings.LANGUAGES: - yield table, f'{col}_{lang}' - - + yield table, f"{col}_{lang}" diff --git a/src/utils/management/commands/generate_sitemaps.py b/src/utils/management/commands/generate_sitemaps.py index 6636675b65..fe5fc69068 100644 --- a/src/utils/management/commands/generate_sitemaps.py +++ b/src/utils/management/commands/generate_sitemaps.py @@ -23,27 +23,26 @@ def add_arguments(self, parser): """ super().add_arguments(parser) parser.add_argument( - '--site_type', - choices=['journals', 'repositories'], - help='The type of site, either journals or repositories', - + "--site_type", + choices=["journals", "repositories"], + help="The type of site, either journals or repositories", ) parser.add_argument( - '--codes', - nargs='+', - help='The codes of the sites (empty for all sites)', + "--codes", + nargs="+", + help="The codes of the sites (empty for all sites)", ) def handle(self, *args, **options): - site_type = options.get('site_type') - codes = options.get('codes') + site_type = options.get("site_type") + codes = options.get("codes") journals = journal_models.Journal.objects.none() repositories = repository_models.Repository.objects.none() - if site_type == 'journals' or not site_type: + if site_type == "journals" or not site_type: journals = journal_models.Journal.objects.all() - if site_type == 'repositories' or not site_type: + if site_type == "repositories" or not site_type: repositories = repository_models.Repository.objects.all() if codes: @@ -76,4 +75,3 @@ def handle(self, *args, **options): for subject in tqdm(repo.subject_set.all()): logic.write_subject_sitemap(subject) - diff --git a/src/utils/management/commands/import_content.py b/src/utils/management/commands/import_content.py index 4a62eadf38..9d859347e7 100755 --- a/src/utils/management/commands/import_content.py +++ b/src/utils/management/commands/import_content.py @@ -13,63 +13,60 @@ def get_nav_children(children): child_list = list() - for litag in children.find_all('li'): - a = litag.find('a', href=True) - child_list.append( - {'text': a.text, - 'href': a.attrs['href']} - ) + for litag in children.find_all("li"): + a = litag.find("a", href=True) + child_list.append({"text": a.text, "href": a.attrs["href"]}) return child_list def get_nav(text, nav, child_identifier): nav_list = list() - soup = BeautifulSoup(text, 'html.parser') - ultag = soup.find('ul', {'class': nav}) + soup = BeautifulSoup(text, "html.parser") + ultag = soup.find("ul", {"class": nav}) - for litag in ultag.find_all('li', {'class': child_identifier}): - children = litag.find('ul') + for litag in ultag.find_all("li", {"class": child_identifier}): + children = litag.find("ul") - a = litag.find('a', href=True) + a = litag.find("a", href=True) nav_list.append( - {'text': a.text, - 'href': a.attrs['href'], - 'children': get_nav_children(children)} + { + "text": a.text, + "href": a.attrs["href"], + "children": get_nav_children(children), + } ) return nav_list def generate_cms_pages(object, nav_items, content): - for item in nav_items: - print('Fetching ', item['href']) - r = requests.get(item['href']) - soup = BeautifulSoup(r.text, 'html.parser') - html = soup.find('div', {'class': content}) + print("Fetching ", item["href"]) + r = requests.get(item["href"]) + soup = BeautifulSoup(r.text, "html.parser") + html = soup.find("div", {"class": content}) if html is not None: content_type = ContentType.objects.get_for_model(object) new_cms_page = cms_models.Page.objects.create( content_type=content_type, object_id=object.pk, - name=slugify(item.get('text')), - display_name=item.get('text'), + name=slugify(item.get("text")), + display_name=item.get("text"), content=html.prettify(), is_markdown=False, ) - nav_item = generate_nav_entry(object, - new_cms_page, - True if item['children'] else False, - False) + nav_item = generate_nav_entry( + object, new_cms_page, True if item["children"] else False, False + ) - for child in item['children']: - print('Fetching ', child['href']) - r = requests.get(child['href']) - soup = BeautifulSoup(r.text, 'html.parser') - html = soup.find('div', {'class': content}) - name = "{0}/{1}".format(new_cms_page.name, slugify(child.get('text'))) + for child in item["children"]: + print("Fetching ", child["href"]) + r = requests.get(child["href"]) + soup = BeautifulSoup(r.text, "html.parser") + html = soup.find("div", {"class": content}) + name = "{0}/{1}".format(new_cms_page.name, slugify(child.get("text"))) if html is not None: content_type = ContentType.objects.get_for_model(object) @@ -77,20 +74,19 @@ def generate_cms_pages(object, nav_items, content): content_type=content_type, object_id=object.pk, name=name, - display_name=child.get('text'), + display_name=child.get("text"), content=html.prettify(), is_markdown=False, ) - generate_nav_entry(object, - child_cms_page, - False, - new_cms_page, - nav_item) - + generate_nav_entry( + object, child_cms_page, False, new_cms_page, nav_item + ) -def generate_nav_entry(object, cms_page, has_children=False, parent=None, parent_nav_item=None): +def generate_nav_entry( + object, cms_page, has_children=False, parent=None, parent_nav_item=None +): new_nav_item = cms_models.NavigationItem.objects.create( object=object, link_name=cms_page.display_name, @@ -115,11 +111,11 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--journal_id', default=False) - parser.add_argument('--url') - parser.add_argument('--ul') - parser.add_argument('--child_identifier') - parser.add_argument('--content') + parser.add_argument("--journal_id", default=False) + parser.add_argument("--url") + parser.add_argument("--ul") + parser.add_argument("--child_identifier") + parser.add_argument("--content") def handle(self, *args, **options): """Takes a UL and CONTENT arg to import content into the CMS. @@ -128,19 +124,19 @@ def handle(self, *args, **options): :param options: None :return: None """ - journal_id = options.get('journal_id') - url = options.get('url') - ul = options.get('ul') - child_identifier = options.get('child_identifier') - content = options.get('content') + journal_id = options.get("journal_id") + url = options.get("url") + ul = options.get("ul") + child_identifier = options.get("child_identifier") + content = options.get("content") - if journal_id == 'press': + if journal_id == "press": object = press_models.Press.objects.all()[0] else: object = journal_models.Journal.objects.filter(pk=journal_id) if not object: - print('No object (journal or press) was found.') + print("No object (journal or press) was found.") sys.exit() print("Object to generate nav for {0}".format(object)) diff --git a/src/utils/management/commands/import_issue_images.py b/src/utils/management/commands/import_issue_images.py index bc0f53f62d..9bd3ec2abe 100755 --- a/src/utils/management/commands/import_issue_images.py +++ b/src/utils/management/commands/import_issue_images.py @@ -14,9 +14,9 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_id') - parser.add_argument('user_id') + parser.add_argument("url") + parser.add_argument("journal_id") + parser.add_argument("user_id") def handle(self, *args, **options): """Imports a set of UP issue images into Janeway. diff --git a/src/utils/management/commands/import_jms_articles.py b/src/utils/management/commands/import_jms_articles.py index d53d3cc857..42422bf9ed 100755 --- a/src/utils/management/commands/import_jms_articles.py +++ b/src/utils/management/commands/import_jms_articles.py @@ -16,10 +16,10 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code') - parser.add_argument('url') - parser.add_argument('article_type') - parser.add_argument('auth_file') + parser.add_argument("journal_code") + parser.add_argument("url") + parser.add_argument("article_type") + parser.add_argument("auth_file") def handle(self, *args, **options): """Imports a set of UP journal-level metadata into Janeway. @@ -28,15 +28,25 @@ def handle(self, *args, **options): :param options: Dictionary containing 'url', 'journal_id', and a 'user_id' :return: None """ - articles = importer.up.get_article_list(options.get("url"), options.get("article_type"), - options.get("auth_file")) + articles = importer.up.get_article_list( + options.get("url"), options.get("article_type"), options.get("auth_file") + ) for article in articles: - - if options.get("article_type") == 'in_review': - call_command('up_import_review_article', options.get("journal_code"), options.get('url'), article, - options.get('auth_file')) - - if options.get('article_type') == 'in_editing': - call_command('up_import_editing_article', options.get("journal_code"), options.get('url'), article, - options.get('auth_file')) + if options.get("article_type") == "in_review": + call_command( + "up_import_review_article", + options.get("journal_code"), + options.get("url"), + article, + options.get("auth_file"), + ) + + if options.get("article_type") == "in_editing": + call_command( + "up_import_editing_article", + options.get("journal_code"), + options.get("url"), + article, + options.get("auth_file"), + ) diff --git a/src/utils/management/commands/import_jms_user.py b/src/utils/management/commands/import_jms_user.py index 48c6d2dde4..cdc29fecea 100644 --- a/src/utils/management/commands/import_jms_user.py +++ b/src/utils/management/commands/import_jms_user.py @@ -18,11 +18,11 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', default=None) - parser.add_argument('base_url', default=None) - parser.add_argument('user_id', default=None) - parser.add_argument('auth_file', default=None) - parser.add_argument('--nuke', action='store_true', dest='nuke') + parser.add_argument("journal_code", default=None) + parser.add_argument("base_url", default=None) + parser.add_argument("user_id", default=None) + parser.add_argument("auth_file", default=None) + parser.add_argument("--nuke", action="store_true", dest="nuke") def handle(self, *args, **options): """Fetches a backend article from UP. @@ -31,17 +31,20 @@ def handle(self, *args, **options): :param options: None :return: None """ - if options.get('nuke'): - management.call_command('nuke_import_cache') + if options.get("nuke"): + management.call_command("nuke_import_cache") - url = '{base_url}/jms/manager/userProfile/{user_id}'.format(base_url=options.get('base_url'), - user_id=options.get('user_id')) + url = "{base_url}/jms/manager/userProfile/{user_id}".format( + base_url=options.get("base_url"), user_id=options.get("user_id") + ) try: - journal = models.Journal.objects.get(code=options.get('journal_code')) - import_jms_user(url, - journal, - auth_file=options.get('auth_file'), - base_url=options.get('base_url'), - user_id=options.get('user_id')) + journal = models.Journal.objects.get(code=options.get("journal_code")) + import_jms_user( + url, + journal, + auth_file=options.get("auth_file"), + base_url=options.get("base_url"), + user_id=options.get("user_id"), + ) except models.Journal.DoesNotExist: - print('Journal not found.') + print("Journal not found.") diff --git a/src/utils/management/commands/import_jms_users.py b/src/utils/management/commands/import_jms_users.py index 4d92de2293..5b4029c494 100755 --- a/src/utils/management/commands/import_jms_users.py +++ b/src/utils/management/commands/import_jms_users.py @@ -16,9 +16,9 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code') - parser.add_argument('url') - parser.add_argument('auth_file') + parser.add_argument("journal_code") + parser.add_argument("url") + parser.add_argument("auth_file") def handle(self, *args, **options): """Imports a set of UP journal-level metadata into Janeway. @@ -30,5 +30,10 @@ def handle(self, *args, **options): users = importer.up.get_user_list(options.get("url"), options.get("auth_file")) for user in users: - call_command('import_jms_user', options.get("journal_code"), options.get('url'), user, - options.get('auth_file')) + call_command( + "import_jms_user", + options.get("journal_code"), + options.get("url"), + user, + options.get("auth_file"), + ) diff --git a/src/utils/management/commands/import_journal_metadata.py b/src/utils/management/commands/import_journal_metadata.py index ef9eec3b77..9063c85c5b 100755 --- a/src/utils/management/commands/import_journal_metadata.py +++ b/src/utils/management/commands/import_journal_metadata.py @@ -14,9 +14,9 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_id') - parser.add_argument('user_id') + parser.add_argument("url") + parser.add_argument("journal_id") + parser.add_argument("user_id") def handle(self, *args, **options): """Imports a set of UP journal-level metadata into Janeway. diff --git a/src/utils/management/commands/install_janeway.py b/src/utils/management/commands/install_janeway.py index 8d4c578911..058a50ab0c 100755 --- a/src/utils/management/commands/install_janeway.py +++ b/src/utils/management/commands/install_janeway.py @@ -10,13 +10,14 @@ from press import models as press_models from journal import models as journal_models from utils.install import ( - update_issue_types, - update_settings, - update_xsl_files, + update_issue_types, + update_settings, + update_xsl_files, ) from utils import shared -ROLES_RELATIVE_PATH = 'utils/install/roles.json' +ROLES_RELATIVE_PATH = "utils/install/roles.json" + class Command(BaseCommand): """ @@ -27,60 +28,63 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-d', '--dry-run', - action='store_true', - dest='dry_run', + "-d", + "--dry-run", + action="store_true", + dest="dry_run", default=False, - help='Rolls back the transaction resulting from this command' + help="Rolls back the transaction resulting from this command", ) parser.add_argument( - '--use-defaults', - action='store_true', - dest='use_defaults', + "--use-defaults", + action="store_true", + dest="use_defaults", default=False, - help='Avoids requesting user input and uses default details (defaults can be set with environment variables)' + help="Avoids requesting user input and uses default details (defaults can be set with environment variables)", ) parser.add_argument( - '--press_name', - dest='press_name', - default=os.getenv("JANEWAY_PRESS_NAME", default='Press'), - help='Specifies the Press Name to use when installing Janeway' + "--press_name", + dest="press_name", + default=os.getenv("JANEWAY_PRESS_NAME", default="Press"), + help="Specifies the Press Name to use when installing Janeway", ) parser.add_argument( - '--press_domain', - dest='press_domain', - default=os.getenv("JANEWAY_PRESS_DOMAIN", default='localhost'), - help='Specifies the Press Domain to use when installing Janeway' + "--press_domain", + dest="press_domain", + default=os.getenv("JANEWAY_PRESS_DOMAIN", default="localhost"), + help="Specifies the Press Domain to use when installing Janeway", ) parser.add_argument( - '--press_contact', - dest='press_contact', - default=os.getenv("JANEWAY_PRESS_CONTACT", default='dev@noemail.com'), - help='Specifies the Press Contact email address to use when installing Janeway' + "--press_contact", + dest="press_contact", + default=os.getenv("JANEWAY_PRESS_CONTACT", default="dev@noemail.com"), + help="Specifies the Press Contact email address to use when installing Janeway", ) parser.add_argument( - '--journal_code', - dest='journal_code', - default=os.getenv("JANEWAY_JOURNAL_CODE", default='Journal'), - help='Specifies the Journal Code to use when installing Janeway' + "--journal_code", + dest="journal_code", + default=os.getenv("JANEWAY_JOURNAL_CODE", default="Journal"), + help="Specifies the Journal Code to use when installing Janeway", ) parser.add_argument( - '--journal_name', - dest='journal_name', - default=os.getenv("JANEWAY_JOURNAL_NAME", default='Test Journal'), - help='Specifies the Journal Name to use when installing Janeway' + "--journal_name", + dest="journal_name", + default=os.getenv("JANEWAY_JOURNAL_NAME", default="Test Journal"), + help="Specifies the Journal Name to use when installing Janeway", ) parser.add_argument( - '--journal_domain', - dest='journal_domain', - default=os.getenv("JANEWAY_JOURNAL_DOMAIN", default=''), - help='Specifies the Journal Domain to use when installing Janeway (optional)' + "--journal_domain", + dest="journal_domain", + default=os.getenv("JANEWAY_JOURNAL_DOMAIN", default=""), + help="Specifies the Journal Domain to use when installing Janeway (optional)", ) parser.add_argument( - '--journal_description', - dest='journal_description', - default=os.getenv("JANEWAY_JOURNAL_DESCRIPTION", default='Journal #1 description'), - help='Specifies the Journal Description to use when installing Janeway' + "--journal_description", + dest="journal_description", + default=os.getenv( + "JANEWAY_JOURNAL_DESCRIPTION", default="Journal #1 description" + ), + help="Specifies the Journal Description to use when installing Janeway", ) def handle(self, *args, **options): @@ -96,21 +100,21 @@ def handle(self, *args, **options): raise ImproperlyConfigured("USE_I18N must be enabled from v1.4 of Janeway.") use_defaults = options["use_defaults"] - call_command('migrate') + call_command("migrate") print("Please answer the following questions.\n") - translation.activate('en') + translation.activate("en") with transaction.atomic(): press = press_models.Press.objects.first() if not press: press = press_models.Press() if use_defaults: - press.name = options['press_name'] - press.domain = options['press_domain'] - press.main_contact= options['press_contact'] + press.name = options["press_name"] + press.domain = options["press_domain"] + press.main_contact = options["press_contact"] else: - press.name = input('Press name: ') - press.domain = input('Press domain: ') - press.main_contact = input('Press main contact (email): ') + press.name = input("Press name: ") + press.domain = input("Press domain: ") + press.main_contact = input("Press main contact (email): ") press.save() print("Thanks! We will now set up our first journal.\n") @@ -120,56 +124,56 @@ def handle(self, *args, **options): print("[okay]") journal = journal_models.Journal() if use_defaults: - journal.code = options['journal_code'] - journal.domain = options['journal_domain'] + journal.code = options["journal_code"] + journal.domain = options["journal_domain"] else: - journal.code = input('Journal #1 code: ') - journal.domain = input('Journal #1 domain (Optional): ') + journal.code = input("Journal #1 code: ") + journal.domain = input("Journal #1 domain (Optional): ") journal.save() print("Installing issue types fixtures... ", end="") update_issue_types(journal, management_command=False) print("[okay]") print("Installing role fixtures") roles_path = os.path.join(settings.BASE_DIR, ROLES_RELATIVE_PATH) - print('Installing default settings') - call_command('load_default_settings') - call_command('loaddata', roles_path) + print("Installing default settings") + call_command("load_default_settings") + call_command("loaddata", roles_path) if use_defaults: - journal.name = options['journal_name'] - journal.description = options['journal_description'] + journal.name = options["journal_name"] + journal.description = options["journal_description"] else: - journal.name = input('Journal #1 name: ') - journal.description = input('Journal #1 description: ') + journal.name = input("Journal #1 name: ") + journal.description = input("Journal #1 description: ") journal.save() journal.setup_directory() print("Thanks, Journal #1 has been saved.\n") - call_command('show_configured_journals') - call_command('build_assets') + call_command("show_configured_journals") + call_command("build_assets") print("Installing plugins.") - call_command('install_plugins') + call_command("install_plugins") print("Installing Cron jobs") try: - call_command('install_cron') + call_command("install_cron") except FileNotFoundError: self.stderr.write("Error Installing cron") if use_defaults: - print('Don\'t forget to create a superuser: manage.py createsuperuser') + print("Don't forget to create a superuser: manage.py createsuperuser") else: - print('Create a super user.') - call_command('createsuperuser') + print("Create a super user.") + call_command("createsuperuser") - print('Open your browser to your new journal domain ' - '{domain}/install/ to continue this setup process.'.format( + print( + "Open your browser to your new journal domain " + "{domain}/install/ to continue this setup process.".format( domain=journal.domain - if journal.domain - else '{}/{}'.format( - press.domain, journal.code) + if journal.domain + else "{}/{}".format(press.domain, journal.code) ) ) - if options['dry_run'] is True: + if options["dry_run"] is True: print("This was a --dry-run, rolling back...") raise SystemExit() diff --git a/src/utils/management/commands/install_journal.py b/src/utils/management/commands/install_journal.py index 869d3caad4..7968c72f3b 100755 --- a/src/utils/management/commands/install_journal.py +++ b/src/utils/management/commands/install_journal.py @@ -7,23 +7,23 @@ class Command(BaseCommand): - """ A management command to install a new journal.""" + """A management command to install a new journal.""" help = "Installs a journal." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--journal_name', default=False) - parser.add_argument('--journal_code', default=False) - parser.add_argument('--base_url', default=False) - parser.add_argument('--delete', action='store_true', default=False) + parser.add_argument("--journal_name", default=False) + parser.add_argument("--journal_code", default=False) + parser.add_argument("--base_url", default=False) + parser.add_argument("--delete", action="store_true", default=False) def handle(self, *args, **options): - """ Create a new journal on this Janeway install. + """Create a new journal on this Janeway install. :param args: None :param options: Dictionary containing keys '--journal_name', '--journal_code' and '--base_url'. If any of these @@ -32,22 +32,37 @@ def handle(self, *args, **options): :return: None """ - translation.activate('en') - delete = True if options.get('delete') else False - journal_name = options.get('journal_name') if options.get('journal_name') else input( - 'Enter the full name of the Journal: ') - journal_code = options.get('journal_code') if options.get('journal_code') else input( - 'Enter a short name for the Journal: ') - base_url = options.get('base_url') if options.get('base_url') else input( - 'Enter a base url for the Journal: ') + translation.activate("en") + delete = True if options.get("delete") else False + journal_name = ( + options.get("journal_name") + if options.get("journal_name") + else input("Enter the full name of the Journal: ") + ) + journal_code = ( + options.get("journal_code") + if options.get("journal_code") + else input("Enter a short name for the Journal: ") + ) + base_url = ( + options.get("base_url") + if options.get("base_url") + else input("Enter a base url for the Journal: ") + ) if journal_name and journal_code and base_url: - print('Creating new journal {0} ({1}) with domain {2}.'.format(journal_name, journal_code, base_url)) + print( + "Creating new journal {0} ({1}) with domain {2}.".format( + journal_name, journal_code, base_url + ) + ) - install.journal(name=journal_name, code=journal_code, base_url=base_url, delete=delete) + install.journal( + name=journal_name, code=journal_code, base_url=base_url, delete=delete + ) if not delete: journal = journal_models.Journal.objects.get(code=journal_code) install.update_issue_types(journal, management_command=False) - call_command('show_configured_journals') + call_command("show_configured_journals") diff --git a/src/utils/management/commands/install_plugins.py b/src/utils/management/commands/install_plugins.py index 7a8725e987..bc66ffbda2 100755 --- a/src/utils/management/commands/install_plugins.py +++ b/src/utils/management/commands/install_plugins.py @@ -13,40 +13,42 @@ class Command(BaseCommand): help = "Checks for new plugins and installs them." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('plugin_name', nargs='?', default=None) + parser.add_argument("plugin_name", nargs="?", default=None) def handle(self, *args, **options): - """ Checks for new plugins and installs them. + """Checks for new plugins and installs them. :param args: None :param options: None :return: None """ - plugin_name = options.get('plugin_name', None) + plugin_name = options.get("plugin_name", None) if plugin_name: plugin_dirs = [plugin_name] homepage_dirs = [] else: - plugin_dirs = plugin_loader.get_dirs('plugins') + plugin_dirs = plugin_loader.get_dirs("plugins") homepage_dirs = plugin_loader.get_dirs( - os.path.join('core', 'homepage_elements'), + os.path.join("core", "homepage_elements"), ) for plugin in plugin_dirs: - print('Checking plugin {0}'.format(plugin)) + print("Checking plugin {0}".format(plugin)) plugin_module_name = "plugins.{0}.plugin_settings".format(plugin) plugin_settings = import_module(plugin_module_name) plugin_settings.install() for plugin in homepage_dirs: - print('Checking plugin {0}'.format(plugin)) - plugin_module_name = "core.homepage_elements.{0}.plugin_settings".format(plugin) + print("Checking plugin {0}".format(plugin)) + plugin_module_name = "core.homepage_elements.{0}.plugin_settings".format( + plugin + ) plugin_settings = import_module(plugin_module_name) plugin_settings.install() diff --git a/src/utils/management/commands/list_backend_articles.py b/src/utils/management/commands/list_backend_articles.py index ff5d47e047..e6d47cea5f 100755 --- a/src/utils/management/commands/list_backend_articles.py +++ b/src/utils/management/commands/list_backend_articles.py @@ -14,9 +14,9 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('article_type') - parser.add_argument('auth_file') + parser.add_argument("url") + parser.add_argument("article_type") + parser.add_argument("auth_file") def handle(self, *args, **options): """Imports a set of UP journal-level metadata into Janeway. @@ -25,4 +25,10 @@ def handle(self, *args, **options): :param options: Dictionary containing 'url', 'journal_id', and a 'user_id' :return: None """ - print(importer.up.get_article_list(options.get("url"), options.get("article_type"), options.get("auth_file"))) + print( + importer.up.get_article_list( + options.get("url"), + options.get("article_type"), + options.get("auth_file"), + ) + ) diff --git a/src/utils/management/commands/list_backend_users.py b/src/utils/management/commands/list_backend_users.py index d9935044e6..ec0fa61f78 100755 --- a/src/utils/management/commands/list_backend_users.py +++ b/src/utils/management/commands/list_backend_users.py @@ -14,8 +14,8 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('auth_file') + parser.add_argument("url") + parser.add_argument("auth_file") def handle(self, *args, **options): """Imports a set of UP journal-level metadata into Janeway. diff --git a/src/utils/management/commands/load_default_settings.py b/src/utils/management/commands/load_default_settings.py index aeb45437cd..9b4f92c328 100644 --- a/src/utils/management/commands/load_default_settings.py +++ b/src/utils/management/commands/load_default_settings.py @@ -5,22 +5,25 @@ class Command(BaseCommand): - """ Loads the default values for Janeway settings """ + """Loads the default values for Janeway settings""" - help = ("Loads the default values for Janeway settings. " - "If ran multiple times, it will only load missing entries") + help = ( + "Loads the default values for Janeway settings. " + "If ran multiple times, it will only load missing entries" + ) def add_arguments(self, parser): - parser.add_argument('--force_update', - action='store_true', - dest='force_update', - default=False, - help='Resets all of the default settings from the value in JSON.') + parser.add_argument( + "--force_update", + action="store_true", + dest="force_update", + default=False, + help="Resets all of the default settings from the value in JSON.", + ) def handle(self, *args, **options): - - translation.activate('en') + translation.activate("en") install.update_settings( management_command=True, - overwrite_with_defaults=options.get('force_update', False), + overwrite_with_defaults=options.get("force_update", False), ) diff --git a/src/utils/management/commands/migrate_plugins.py b/src/utils/management/commands/migrate_plugins.py index 01fc754a19..1128aa95d5 100755 --- a/src/utils/management/commands/migrate_plugins.py +++ b/src/utils/management/commands/migrate_plugins.py @@ -12,17 +12,17 @@ class Command(BaseCommand): help = "Migrates plugins" def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--make', action='store_true', default=False) - parser.add_argument('--migrate', action='store_true', default=False) - parser.add_argument('--app_name', default=False) + parser.add_argument("--make", action="store_true", default=False) + parser.add_argument("--migrate", action="store_true", default=False) + parser.add_argument("--app_name", default=False) def handle(self, *args, **options): - """ A management command to check for and migrate plugins. + """A management command to check for and migrate plugins. :param args: None :param options: None @@ -30,25 +30,27 @@ def handle(self, *args, **options): """ all_plugins_to_handle = None - if not options.get('app_name'): - plugin_dirs = plugin_loader.get_dirs('plugins') - homepage_dirs = plugin_loader.get_dirs(os.path.join("core", "homepage_elements")) + if not options.get("app_name"): + plugin_dirs = plugin_loader.get_dirs("plugins") + homepage_dirs = plugin_loader.get_dirs( + os.path.join("core", "homepage_elements") + ) all_plugins_to_handle = plugin_dirs + homepage_dirs - elif options.get('app_name'): - all_plugins_to_handle = [options.get('app_name')] + elif options.get("app_name"): + all_plugins_to_handle = [options.get("app_name")] if all_plugins_to_handle: - for plugin in all_plugins_to_handle: - - if options.get('make'): - call_command('makemigrations', plugin) - elif options.get('migrate'): + if options.get("make"): + call_command("makemigrations", plugin) + elif options.get("migrate"): try: - call_command('migrate', plugin) + call_command("migrate", plugin) except CommandError: - print('Plugin {0} has no migrations.'.format(plugin)) + print("Plugin {0} has no migrations.".format(plugin)) else: - print('Result: Either there are no plugins installed or the app_name you passed doesn\'t exist') + print( + "Result: Either there are no plugins installed or the app_name you passed doesn't exist" + ) diff --git a/src/utils/management/commands/move_press.py b/src/utils/management/commands/move_press.py index a4a9ee7d34..a04cd89cc6 100755 --- a/src/utils/management/commands/move_press.py +++ b/src/utils/management/commands/move_press.py @@ -14,16 +14,15 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('new_url') + parser.add_argument("new_url") def handle(self, *args, **options): - """ Moves your press to a new URL." + """Moves your press to a new URL." :param args: None :param options: Dictionary containing a 'new_url' string to which the press will be relocated :return: None """ p = press_models.Press.get_press(None) - p.domain = options['new_url'] + p.domain = options["new_url"] p.save() - diff --git a/src/utils/management/commands/nuke_db.py b/src/utils/management/commands/nuke_db.py index 5bfb0c839d..1a3a767406 100755 --- a/src/utils/management/commands/nuke_db.py +++ b/src/utils/management/commands/nuke_db.py @@ -6,12 +6,12 @@ class Command(BaseCommand): - """ A management command to nuke the current DB.""" + """A management command to nuke the current DB.""" help = "Nukes the current DB. Deletes all journals and settings. Dangerous." def handle(self, *args, **options): - """ Deletes all current journals and reinstalls the press. + """Deletes all current journals and reinstalls the press. :param args: None :param options: None. @@ -23,4 +23,4 @@ def handle(self, *args, **options): core_models.SettingValue.objects.all().delete() core_models.Setting.objects.all().delete() - call_command('show_configured_journals') + call_command("show_configured_journals") diff --git a/src/utils/management/commands/nuke_import_cache.py b/src/utils/management/commands/nuke_import_cache.py index e77ab80022..0b4a21c0ba 100755 --- a/src/utils/management/commands/nuke_import_cache.py +++ b/src/utils/management/commands/nuke_import_cache.py @@ -4,12 +4,12 @@ class Command(BaseCommand): - """ A management command to nuke the current import cache.""" + """A management command to nuke the current import cache.""" help = "Nukes the current import cache in its entirety." def handle(self, *args, **options): - """ Deletes all import cache entries in the DB and on disk + """Deletes all import cache entries in the DB and on disk :param args: None :param options: None. diff --git a/src/utils/management/commands/register_all_crossref.py b/src/utils/management/commands/register_all_crossref.py index cfaf9efab4..1c88cfb3ec 100755 --- a/src/utils/management/commands/register_all_crossref.py +++ b/src/utils/management/commands/register_all_crossref.py @@ -18,7 +18,7 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code') + parser.add_argument("journal_code") def handle(self, *args, **options): """Calls the Crossref registration options @@ -28,7 +28,7 @@ def handle(self, *args, **options): :return: None """ journal = journal_models.Journal.objects.get( - code=options.get('journal_code'), + code=options.get("journal_code"), ) articles = submission_models.Article.objects.filter( journal=journal, @@ -36,21 +36,23 @@ def handle(self, *args, **options): ) for article in articles: - print('Handling article {0}'.format(article.pk)) + print("Handling article {0}".format(article.pk)) if article.is_published: - print('Article is published') + print("Article is published") try: - identifier = article.get_identifier('doi', object=True) + identifier = article.get_identifier("doi", object=True) if identifier and identifier.is_doi: identifier.register() else: - identifier = identifier_logic.generate_crossref_doi_with_pattern(article) + identifier = ( + identifier_logic.generate_crossref_doi_with_pattern(article) + ) identifier.register() except AttributeError as e: - print('Error {0}'.format(e)) + print("Error {0}".format(e)) else: - print('Article {} is not published.'.format(article.pk)) + print("Article {} is not published.".format(article.pk)) time.sleep(1) diff --git a/src/utils/management/commands/register_crossref_doi.py b/src/utils/management/commands/register_crossref_doi.py index 206f152a91..07ff7c198b 100755 --- a/src/utils/management/commands/register_crossref_doi.py +++ b/src/utils/management/commands/register_crossref_doi.py @@ -15,7 +15,7 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('article_id') + parser.add_argument("article_id") def handle(self, *args, **options): """Calls the Crossref registration options @@ -24,9 +24,9 @@ def handle(self, *args, **options): :param options: Dictionary containing 'article_id' :return: None """ - article = submission_models.Article.objects.get(pk=options['article_id']) + article = submission_models.Article.objects.get(pk=options["article_id"]) - identifier = article.get_identifier('doi', object=True) + identifier = article.get_identifier("doi", object=True) if identifier.is_doi: identifier.register() diff --git a/src/utils/management/commands/reset_frozen_authors.py b/src/utils/management/commands/reset_frozen_authors.py index 0f0b459de8..b427363b44 100755 --- a/src/utils/management/commands/reset_frozen_authors.py +++ b/src/utils/management/commands/reset_frozen_authors.py @@ -4,7 +4,7 @@ class Command(BaseCommand): - """ A management command to reset frozen author records with live profile records.""" + """A management command to reset frozen author records with live profile records.""" help = "Resets frozen author records with fresh data." @@ -14,14 +14,15 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument("--hard", - action='store_true', - default=False, - help="Also deletes manually added frozen records" + parser.add_argument( + "--hard", + action="store_true", + default=False, + help="Also deletes manually added frozen records", ) def handle(self, *args, **options): - """ Resets frozen author records with live profile records. + """Resets frozen author records with live profile records. :param args: None :param options: None. @@ -36,18 +37,19 @@ def handle(self, *args, **options): for article in articles: for author in article.authors.all(): - frozen_dict = { - 'article': article, - 'author': author, - 'first_name': author.first_name, - 'middle_name': author.middle_name, - 'last_name': author.last_name, - 'institution': author.institution, - 'department': author.department, + "article": article, + "author": author, + "first_name": author.first_name, + "middle_name": author.middle_name, + "last_name": author.last_name, + "institution": author.institution, + "department": author.department, } - frozen_author = submission_models.FrozenAuthor.objects.create(**frozen_dict) + frozen_author = submission_models.FrozenAuthor.objects.create( + **frozen_dict + ) author.frozen_author = frozen_author author.save() diff --git a/src/utils/management/commands/restore_defaults_to_journals.py b/src/utils/management/commands/restore_defaults_to_journals.py index b645695826..5568798abb 100755 --- a/src/utils/management/commands/restore_defaults_to_journals.py +++ b/src/utils/management/commands/restore_defaults_to_journals.py @@ -22,5 +22,11 @@ def handle(self, *args, **options): print("Journals:") for journal in journals: - install.update_settings(journal, management_command=True, overwrite_with_defaults=True) - print('Journal with ID {0} [{1}]: {2}. SETTINGS SYNCED.'.format(journal.id, journal.name, journal.domain)) + install.update_settings( + journal, management_command=True, overwrite_with_defaults=True + ) + print( + "Journal with ID {0} [{1}]: {2}. SETTINGS SYNCED.".format( + journal.id, journal.name, journal.domain + ) + ) diff --git a/src/utils/management/commands/run_upgrade.py b/src/utils/management/commands/run_upgrade.py index c228a746da..13fc8fec44 100644 --- a/src/utils/management/commands/run_upgrade.py +++ b/src/utils/management/commands/run_upgrade.py @@ -7,7 +7,7 @@ def get_modules(): - path = os.path.join(settings.BASE_DIR, 'utils', 'upgrade') + path = os.path.join(settings.BASE_DIR, "utils", "upgrade") root, dirs, files = next(os.walk(path)) return files @@ -25,25 +25,30 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--path', required=False) + parser.add_argument("--path", required=False) def handle(self, *args, **options): - - raise DeprecationWarning('This command is deprecated. Use the update script .update.sh') - if not options.get('path'): - print('No upgrade selected. Available upgrade paths: ') + raise DeprecationWarning( + "This command is deprecated. Use the update script .update.sh" + ) + if not options.get("path"): + print("No upgrade selected. Available upgrade paths: ") for file in get_modules(): - module_name = file.split('.')[0] - print('- {module_name}'.format(module_name=module_name)) - print('To run an upgrade use the following: `python3 manage.py run_upgrade --script 12_13`') + module_name = file.split(".")[0] + print("- {module_name}".format(module_name=module_name)) + print( + "To run an upgrade use the following: `python3 manage.py run_upgrade --script 12_13`" + ) else: - translation.activate('en') - upgrade_module_name = options.get('path') - upgrade_module_path = 'utils.upgrade.{module_name}'.format(module_name=upgrade_module_name) + translation.activate("en") + upgrade_module_name = options.get("path") + upgrade_module_path = "utils.upgrade.{module_name}".format( + module_name=upgrade_module_name + ) try: upgrade_module = import_module(upgrade_module_path) upgrade_module.execute() except ImportError as e: - print('There was an error running the requested upgrade: ') + print("There was an error running the requested upgrade: ") print(e) diff --git a/src/utils/management/commands/scrape_oai.py b/src/utils/management/commands/scrape_oai.py index c1ff167a2e..f2865d6565 100755 --- a/src/utils/management/commands/scrape_oai.py +++ b/src/utils/management/commands/scrape_oai.py @@ -15,19 +15,24 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_id') - parser.add_argument('user_id') - parser.add_argument('-u', '--update', - action='store_true', - dest='update', - default=False, - help='Updates metadata if the item already exists') - parser.add_argument('--delete', - action='store_true', - dest='delete', - default=False, - help='Delete all articles and non-superusers in the database before import') + parser.add_argument("url") + parser.add_argument("journal_id") + parser.add_argument("user_id") + parser.add_argument( + "-u", + "--update", + action="store_true", + dest="update", + default=False, + help="Updates metadata if the item already exists", + ) + parser.add_argument( + "--delete", + action="store_true", + dest="delete", + default=False, + help="Delete all articles and non-superusers in the database before import", + ) def handle(self, *args, **options): """Imports an OAI feed into Janeway. @@ -36,5 +41,5 @@ def handle(self, *args, **options): :param options: Dictionary containing 'url', 'journal_id', 'user_id', and a boolean '--delete' flag :return: None """ - translation.activate('en') + translation.activate("en") importer.import_oai(**options) diff --git a/src/utils/management/commands/scrape_ojs_page.py b/src/utils/management/commands/scrape_ojs_page.py index 5b1fbc9c79..594afa6f56 100755 --- a/src/utils/management/commands/scrape_ojs_page.py +++ b/src/utils/management/commands/scrape_ojs_page.py @@ -4,7 +4,7 @@ class Command(BaseCommand): - """ Takes an OJS url and imports it into Janeway.""" + """Takes an OJS url and imports it into Janeway.""" help = "Takes an OJS url and pulls information into Janeway." @@ -14,14 +14,16 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_id') - parser.add_argument('user_id') - parser.add_argument('--delete', - action='store_true', - dest='delete', - default=False, - help='Delete all articles and non-superusers in the database before import') + parser.add_argument("url") + parser.add_argument("journal_id") + parser.add_argument("user_id") + parser.add_argument( + "--delete", + action="store_true", + dest="delete", + default=False, + help="Delete all articles and non-superusers in the database before import", + ) def handle(self, *args, **options): """Imports an OJS article into Janeway. diff --git a/src/utils/management/commands/scrape_up_all.py b/src/utils/management/commands/scrape_up_all.py index 72619ac72c..31ec1aace1 100755 --- a/src/utils/management/commands/scrape_up_all.py +++ b/src/utils/management/commands/scrape_up_all.py @@ -4,7 +4,7 @@ class Command(BaseCommand): - """ Takes a Ubiquity Press url and imports it into Janeway.""" + """Takes a Ubiquity Press url and imports it into Janeway.""" help = "Takes an UP url and pulls information into Janeway." @@ -14,22 +14,24 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_code') - parser.add_argument('user_id') + parser.add_argument("url") + parser.add_argument("journal_code") + parser.add_argument("user_id") parser.add_argument( - '--delete', - action='store_true', - dest='delete', + "--delete", + action="store_true", + dest="delete", default=1, - help='Delete all articles and non-superusers in the database ' - 'before import', + help="Delete all articles and non-superusers in the database " + "before import", ) - parser.add_argument('-u', '--update', - action='store_true', - dest='update', - default=False, - help='Updates metadata if the item already exists', + parser.add_argument( + "-u", + "--update", + action="store_true", + dest="update", + default=False, + help="Updates metadata if the item already exists", ) def handle(self, *args, **options): @@ -40,4 +42,3 @@ def handle(self, *args, **options): :return: None """ importer.import_all(**options) - diff --git a/src/utils/management/commands/scrape_up_cms_page.py b/src/utils/management/commands/scrape_up_cms_page.py index dc75d8b214..04355cfd93 100644 --- a/src/utils/management/commands/scrape_up_cms_page.py +++ b/src/utils/management/commands/scrape_up_cms_page.py @@ -1,4 +1,3 @@ - from django.core.management.base import BaseCommand from django.utils import translation @@ -10,6 +9,7 @@ class Command(BaseCommand): """ Scrapes a given CMS page from an UP journal """ + help = "Scrapes a given CMS page from an UP journal" def add_arguments(self, parser): @@ -18,22 +18,19 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('page_url', default=None) + parser.add_argument("page_url", default=None) parser.add_argument( - 'page_name', + "page_name", help="The display name for the page in Janeway", ) - parser.add_argument('journal_code', default=None) + parser.add_argument("journal_code", default=None) def handle(self, *args, **options): - translation.activate('en') + translation.activate("en") try: - journal = models.Journal.objects.get( - code=options.get('journal_code') - ) + journal = models.Journal.objects.get(code=options.get("journal_code")) except models.Journal.DoesNotExist: - exit('[Error] No journal found with that code.') + exit("[Error] No journal found with that code.") else: - print('Scraping into {}'.format(journal.name)) + print("Scraping into {}".format(journal.name)) scrape_cms_page(journal, options["page_url"], options["page_name"]) - diff --git a/src/utils/management/commands/scrape_up_collections.py b/src/utils/management/commands/scrape_up_collections.py index 7fd5cd3815..7cc1a6fa7c 100755 --- a/src/utils/management/commands/scrape_up_collections.py +++ b/src/utils/management/commands/scrape_up_collections.py @@ -7,7 +7,7 @@ class Command(BaseCommand): - """ Takes a UP journal and imports its collections into Janeway.""" + """Takes a UP journal and imports its collections into Janeway.""" help = "Takes an UP url and pulls collections nto Janeway." @@ -17,14 +17,16 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_code') - parser.add_argument('user_id') - parser.add_argument('-u', '--update', - action='store_true', - dest='update', - default=False, - help='Updates metadata if the item already exists', + parser.add_argument("url") + parser.add_argument("journal_code") + parser.add_argument("user_id") + parser.add_argument( + "-u", + "--update", + action="store_true", + dest="update", + default=False, + help="Updates metadata if the item already exists", ) def handle(self, *args, **options): @@ -38,4 +40,3 @@ def handle(self, *args, **options): owner = Account.objects.get(pk=options["user_id"]) update = options["update"] up.import_collections(journal, options["url"], owner, update=update) - diff --git a/src/utils/management/commands/scrape_up_minutia.py b/src/utils/management/commands/scrape_up_minutia.py index ca6f32a89d..fa761e4294 100644 --- a/src/utils/management/commands/scrape_up_minutia.py +++ b/src/utils/management/commands/scrape_up_minutia.py @@ -15,6 +15,7 @@ class Command(BaseCommand): """ Fetches a backend article from a UP journal """ + help = "Fetches front end minutia from a UP site." def add_arguments(self, parser): @@ -23,36 +24,28 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('base_url', default=None) - parser.add_argument('journal_code', default=None) + parser.add_argument("base_url", default=None) + parser.add_argument("journal_code", default=None) def handle(self, *args, **options): - translation.activate('en') + translation.activate("en") try: - journal = models.Journal.objects.get( - code=options.get('journal_code') - ) - print('Scraping into {}'.format(journal.name)) - scrape_editorial_team( - journal, - options.get('base_url') - ) + journal = models.Journal.objects.get(code=options.get("journal_code")) + print("Scraping into {}".format(journal.name)) + scrape_editorial_team(journal, options.get("base_url")) scrape_policies_page( journal, - options.get('base_url'), - ) - scrape_submissions_page( - journal, - options.get('base_url') + options.get("base_url"), ) + scrape_submissions_page(journal, options.get("base_url")) scrape_research_integrity_page( journal, - options.get('base_url'), + options.get("base_url"), ) scrape_about_page( journal, - options.get('base_url'), + options.get("base_url"), ) except models.Journal.DoesNotExist: - exit('[Error] No journal found with that code.') + exit("[Error] No journal found with that code.") diff --git a/src/utils/management/commands/scrape_up_page.py b/src/utils/management/commands/scrape_up_page.py index b51709161d..5d9b5b1453 100755 --- a/src/utils/management/commands/scrape_up_page.py +++ b/src/utils/management/commands/scrape_up_page.py @@ -4,7 +4,7 @@ class Command(BaseCommand): - """ Takes a Ubiquity Press url and imports it into Janeway.""" + """Takes a Ubiquity Press url and imports it into Janeway.""" help = "Takes an UP url and pulls information into Janeway." @@ -14,23 +14,30 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('url') - parser.add_argument('journal_id') - parser.add_argument('user_id') - parser.add_argument('--delete', - action='store_true', - dest='delete', - default=False, - help='Delete all articles and non-superusers in the database before import') - parser.add_argument('-u', '--update', - action='store_true', - dest='update', - default=False, - help='Updates metadata if the item already exists') - parser.add_argument("-i", "--import-type", - choices=["article", "issue"], - default="article", - help="The type of structure to be imported", + parser.add_argument("url") + parser.add_argument("journal_id") + parser.add_argument("user_id") + parser.add_argument( + "--delete", + action="store_true", + dest="delete", + default=False, + help="Delete all articles and non-superusers in the database before import", + ) + parser.add_argument( + "-u", + "--update", + action="store_true", + dest="update", + default=False, + help="Updates metadata if the item already exists", + ) + parser.add_argument( + "-i", + "--import-type", + choices=["article", "issue"], + default="article", + help="The type of structure to be imported", ) def handle(self, *args, **options): @@ -46,4 +53,3 @@ def handle(self, *args, **options): importer.import_issue_images(**options) else: self.sys.stderr("Unknown import type: %s" % options["import_type"]) - diff --git a/src/utils/management/commands/send_email.py b/src/utils/management/commands/send_email.py index aa793c1120..06f9e1e745 100644 --- a/src/utils/management/commands/send_email.py +++ b/src/utils/management/commands/send_email.py @@ -33,97 +33,88 @@ class Command(BaseCommand): There is currently no support for attachments """ - help = 'Sends an email with data passed at command line' + help = "Sends an email with data passed at command line" def add_arguments(self, parser): parser.add_argument( - '--journal', - help='Journal code', + "--journal", + help="Journal code", ) parser.add_argument( - '--repository', - help='Repository short name', + "--repository", + help="Repository short name", ) parser.add_argument( - '--to', + "--to", required=True, - nargs='+', - help='Email addresses of TO recipients', + nargs="+", + help="Email addresses of TO recipients", ) parser.add_argument( - '--cc', - nargs='+', - help='Email addresses of CC recipients', + "--cc", + nargs="+", + help="Email addresses of CC recipients", ) parser.add_argument( - '--bcc', - help='Filepath to JSON file containing a list of email ' - 'addresses of BCC recipients', + "--bcc", + help="Filepath to JSON file containing a list of email " + "addresses of BCC recipients", ) parser.add_argument( - '--replyto', - help='Email address for replies', + "--replyto", + help="Email address for replies", ) + parser.add_argument("--subject", required=True, help="Subject line") parser.add_argument( - '--subject', + "--body", required=True, - help='Subject line' + help="Filepath to UTF-8 text file, with optional HTML markup", ) parser.add_argument( - '--body', - required=True, - help='Filepath to UTF-8 text file, with optional HTML markup', - ) - parser.add_argument( - '--immediately', - help='By default, you get email data in stdout and must confirm to send. ' - 'Pass --immediately to send immediately', - action='store_true', + "--immediately", + help="By default, you get email data in stdout and must confirm to send. " + "Pass --immediately to send immediately", + action="store_true", ) parser.add_argument( - '--batchsize', - help='Batch size for many BCC recipients', + "--batchsize", + help="Batch size for many BCC recipients", type=int, default=50, ) def handle(self, *args, **options): + verbosity = options.get("verbosity", 1) - verbosity = options.get('verbosity', 1) - - journal_code = options.get('journal') + journal_code = options.get("journal") if journal_code: try: - journal = journal_models.Journal.objects.get( - code=journal_code - ) + journal = journal_models.Journal.objects.get(code=journal_code) except journal_models.Journal.DoesNotExist: logger.error( - self.style.ERROR( - f'Journal code not recognised: {journal_code}' - ) + self.style.ERROR(f"Journal code not recognised: {journal_code}") ) if verbosity >= 1: - self.print_help('manage.py', 'send_email') + self.print_help("manage.py", "send_email") return else: journal = None - repository_short_name = options.get('repository') + repository_short_name = options.get("repository") if repository_short_name: try: - repository= repository_models.Repository.objects.get( + repository = repository_models.Repository.objects.get( short_name=repository_short_name ) except repository_models.Repository.DoesNotExist: logger.error( self.style.ERROR( - f'Repository short name not recognised: ' - f'{repository_short_name}' + f"Repository short name not recognised: " + f"{repository_short_name}" ) ) if verbosity >= 1: - self.print_help('manage.py', 'send_email') + self.print_help("manage.py", "send_email") return else: repository = None @@ -142,65 +133,64 @@ def handle(self, *args, **options): else: request.site_type = press - subject = options['subject'] - to = options['to'] + subject = options["subject"] + to = options["to"] - body_file = options['body'] + body_file = options["body"] try: - with open(body_file, 'r') as ref: + with open(body_file, "r") as ref: body = ref.read() except FileNotFoundError: - logger.error(self.style.ERROR(f'File not found at {body_file}')) + logger.error(self.style.ERROR(f"File not found at {body_file}")) if verbosity >= 1: - self.print_help('manage.py', 'send_email') + self.print_help("manage.py", "send_email") return - log_subject = f'"{subject}" sent from {request.site_type} ' \ - 'with Django management command.' + log_subject = ( + f'"{subject}" sent from {request.site_type} ' + "with Django management command." + ) log_dict = { - 'level': 'Info', - 'action_text': log_subject, - 'types': 'Email', - 'target': None, + "level": "Info", + "action_text": log_subject, + "types": "Email", + "target": None, } - cc = options.get('cc') or '' + cc = options.get("cc") or "" - batch_size = options['batchsize'] + batch_size = options["batchsize"] if (len(to) + len(cc)) > batch_size: logger.error( self.style.ERROR( - f'TO and CC must have fewer than {batch_size} addresses' + f"TO and CC must have fewer than {batch_size} addresses" ) ) return - if not options['bcc']: + if not options["bcc"]: bcc = [] else: - bcc_file = options['bcc'] + bcc_file = options["bcc"] try: - with open(bcc_file, 'r') as ref: + with open(bcc_file, "r") as ref: bcc = json.loads(ref.read()) except FileNotFoundError: - logger.error( - self.style.ERROR(f'File not found at {bcc_file}') - ) + logger.error(self.style.ERROR(f"File not found at {bcc_file}")) if verbosity >= 1: - self.print_help('manage.py', 'send_email') + self.print_help("manage.py", "send_email") return if len(bcc) > batch_size: - batch_num = 1 - log_dict['action_text'] = f'{log_subject} (batch {batch_num})' + log_dict["action_text"] = f"{log_subject} (batch {batch_num})" - if options['replyto']: - reply_to = (options['replyto'],) + if options["replyto"]: + reply_to = (options["replyto"],) else: custom_reply_to_setting = setting_handler.get_setting( - 'general', - 'replyto_address', + "general", + "replyto_address", journal, ) if custom_reply_to_setting and custom_reply_to_setting.value: @@ -208,28 +198,28 @@ def handle(self, *args, **options): else: logger.error( self.style.ERROR( - f'--replyto argument or ' - f'replyto_address setting value needed for BCC list ' - f'length over {batch_size}' + f"--replyto argument or " + f"replyto_address setting value needed for BCC list " + f"length over {batch_size}" ) ) return - if not options['immediately']: + if not options["immediately"]: logger.info( - f'\n\n' - f'Preparing to send email from {request.site_type}\n\n' - f'Subject: {subject}\n\n' - f'TO: {to}\n\n' - f'CC: {cc}\n\n' - f'BCC: {bcc}\n\n' - f'Body: \n{body}\n\n' + f"\n\n" + f"Preparing to send email from {request.site_type}\n\n" + f"Subject: {subject}\n\n" + f"TO: {to}\n\n" + f"CC: {cc}\n\n" + f"BCC: {bcc}\n\n" + f"Body: \n{body}\n\n" ) - send = input('Send? (yes/no): ') + send = input("Send? (yes/no): ") - if not send.lower() == 'yes': - logger.info('Email discarded') + if not send.lower() == "yes": + logger.info("Email discarded") return # Send batch 1 @@ -248,12 +238,10 @@ def handle(self, *args, **options): if len(bcc) > batch_size: to = reply_to cc = [] - for i, bcc_chunk in enumerate( - chunked(bcc[batch_size:], batch_size) - ): + for i, bcc_chunk in enumerate(chunked(bcc[batch_size:], batch_size)): time.sleep(0.5) batch_num = i + 2 - log_dict['action_text'] = f'{log_subject} (batch {batch_num})' + log_dict["action_text"] = f"{log_subject} (batch {batch_num})" send_email_with_body_from_user( request, subject, @@ -264,17 +252,16 @@ def handle(self, *args, **options): bcc=bcc_chunk, ) - logger.info(self.style.SUCCESS('Email sent')) + logger.info(self.style.SUCCESS("Email sent")) new_log = utils_models.LogEntry.objects.filter( subject__contains=log_subject ).last() if new_log: - logger.info(f'Log: {new_log}') + logger.info(f"Log: {new_log}") else: logger.warn( self.style.WARNING( - 'Email not logged. You may want to ' - 'double-check it was sent.' + "Email not logged. You may want to " "double-check it was sent." ) ) diff --git a/src/utils/management/commands/show_configured_journals.py b/src/utils/management/commands/show_configured_journals.py index e2960c1c74..44d021706e 100755 --- a/src/utils/management/commands/show_configured_journals.py +++ b/src/utils/management/commands/show_configured_journals.py @@ -11,7 +11,7 @@ class Command(BaseCommand): help = "Shows configured journal URLs." def handle(self, *args, **options): - """ Show existing configuration of journals, press and sites. + """Show existing configuration of journals, press and sites. :param args: None :param options: None @@ -26,4 +26,8 @@ def handle(self, *args, **options): print("\nJournals:") for journal in journals: - print('Journal with ID {0} [{1}]: {2}'.format(journal.id, journal.name, journal.domain)) + print( + "Journal with ID {0} [{1}]: {2}".format( + journal.id, journal.name, journal.domain + ) + ) diff --git a/src/utils/management/commands/show_id.py b/src/utils/management/commands/show_id.py index ba0130773c..e67e32efdf 100755 --- a/src/utils/management/commands/show_id.py +++ b/src/utils/management/commands/show_id.py @@ -14,7 +14,7 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('doi') + parser.add_argument("doi") def handle(self, *args, **options): """Shows a native system ID @@ -23,8 +23,10 @@ def handle(self, *args, **options): :param options: Dictionary containing 'doi_suffix' :return: None """ - doi = options['doi'] + doi = options["doi"] - article = identifier_models.Identifier.objects.filter(id_type='doi', identifier=doi)[0].article + article = identifier_models.Identifier.objects.filter( + id_type="doi", identifier=doi + )[0].article - print('Article ID is {0}'.format(article.id)) + print("Article ID is {0}".format(article.id)) diff --git a/src/utils/management/commands/similarity_check_csv.py b/src/utils/management/commands/similarity_check_csv.py index 10c4c73199..0567e9cd69 100755 --- a/src/utils/management/commands/similarity_check_csv.py +++ b/src/utils/management/commands/similarity_check_csv.py @@ -15,8 +15,8 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('input_file') - parser.add_argument('output_file') + parser.add_argument("input_file") + parser.add_argument("output_file") def handle(self, *args, **options): """Imports a set of DOIs into . @@ -26,17 +26,17 @@ def handle(self, *args, **options): :return: None """ dict = csv.DictReader( - open(options['input_file'], 'r', encoding="utf-8"), + open(options["input_file"], "r", encoding="utf-8"), ) - with open(options['output_file'], 'w', encoding="utf-8") as out_file: + with open(options["output_file"], "w", encoding="utf-8") as out_file: out = csv.writer(out_file) - out.writerow(['DOI', '']) + out.writerow(["DOI", '']) for row in dict: - if 'amazonaws' not in row['URL'] and 'uwp.co.uk' not in row['URL']: - page = shared.fetch_page(row['URL']) + if "amazonaws" not in row["URL"] and "uwp.co.uk" not in row["URL"]: + page = shared.fetch_page(row["URL"]) pdf_url = shared.get_pdf_url(page) - out.writerow([row['DOI'], pdf_url]) + out.writerow([row["DOI"], pdf_url]) out_file.flush() diff --git a/src/utils/management/commands/sort_journals_by_name.py b/src/utils/management/commands/sort_journals_by_name.py index f66e648329..5399c66192 100755 --- a/src/utils/management/commands/sort_journals_by_name.py +++ b/src/utils/management/commands/sort_journals_by_name.py @@ -21,8 +21,8 @@ def handle(self, *args, **options): name_list = list() journals = models.Journal.objects.all() for journal in journals: - name = setting_handler.get_setting('general', 'journal_name', journal).value - journal_dict[name] = journal.pk, + name = setting_handler.get_setting("general", "journal_name", journal).value + journal_dict[name] = (journal.pk,) name_list.append(name) loop = 1 diff --git a/src/utils/management/commands/store_ithenticate_scores.py b/src/utils/management/commands/store_ithenticate_scores.py index 6a72064360..9707ce0faf 100755 --- a/src/utils/management/commands/store_ithenticate_scores.py +++ b/src/utils/management/commands/store_ithenticate_scores.py @@ -12,7 +12,6 @@ class Command(BaseCommand): def handle(self, *args, **options): for journal in journal_models.Journal.objects.filter(is_remote=False): - if ithenticate.ithenticate_is_enabled(journal): print("Processing journal {0}...".format(journal.name)) articles = models.Article.objects.filter( @@ -22,4 +21,8 @@ def handle(self, *args, **options): ) ithenticate.fetch_percentage(journal, articles) else: - print('Ithenticate is not enabled for {journal}. Skipping.'.format(journal=journal.name)) + print( + "Ithenticate is not enabled for {journal}. Skipping.".format( + journal=journal.name + ) + ) diff --git a/src/utils/management/commands/sync_settings_to_journals.py b/src/utils/management/commands/sync_settings_to_journals.py index 8a863fc056..335d054b18 100755 --- a/src/utils/management/commands/sync_settings_to_journals.py +++ b/src/utils/management/commands/sync_settings_to_journals.py @@ -16,12 +16,12 @@ class Command(BaseCommand): help = "Synchronizes unspecified default settings to all journals." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) + parser.add_argument("journal_code", nargs="?", default=None) def handle(self, *args, **options): """Synchronizes settings to journals. @@ -31,20 +31,20 @@ def handle(self, *args, **options): :return: None """ - #We don't need this now that we have a default for the settings + # We don't need this now that we have a default for the settings self.stderr.write("Command deprecated on Janeway > v1.3.3.1") return - translation.activate('en') + translation.activate("en") journals = journal_models.Journal.objects.all() - journal_code = options.get('journal_code', None) + journal_code = options.get("journal_code", None) if journal_code: try: journals = [journal_models.Journal.objects.get(code=journal_code)] except journal_models.Journal.DoesNotExist: journals = None - print('No journal with that code was found.') + print("No journal with that code was found.") if journals: print("Syncing to {0} Journals:".format(len(journals))) @@ -52,15 +52,22 @@ def handle(self, *args, **options): install.update_settings(journal, management_command=True) if not journal_code: - file = open(os.path.join(settings.BASE_DIR, 'utils', 'install', 'press_settings.json'), 'r') + file = open( + os.path.join( + settings.BASE_DIR, "utils", "install", "press_settings.json" + ), + "r", + ) text = json.loads(file.read()) for setting in text: for press in press_models.Press.objects.all(): print("Syncing to {press}".format(press=press.name)) - setting = press_models.PressSetting.objects.get_or_create(press=press, - name=setting['name'], - defaults={ - 'value': setting['value'], - 'is_boolean': setting['is_boolean'] - }) + setting = press_models.PressSetting.objects.get_or_create( + press=press, + name=setting["name"], + defaults={ + "value": setting["value"], + "is_boolean": setting["is_boolean"], + }, + ) diff --git a/src/utils/management/commands/test_bounce_notification.py b/src/utils/management/commands/test_bounce_notification.py index 5522111130..0460fa4489 100644 --- a/src/utils/management/commands/test_bounce_notification.py +++ b/src/utils/management/commands/test_bounce_notification.py @@ -14,9 +14,9 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('log_id', type=int) + parser.add_argument("log_id", type=int) def handle(self, *args, **options): - log_id = options.get('log_id') + log_id = options.get("log_id") log_entry = models.LogEntry.objects.get(pk=log_id) - logic.send_bounce_notification_to_event_actor(log_entry) \ No newline at end of file + logic.send_bounce_notification_to_event_actor(log_entry) diff --git a/src/utils/management/commands/test_fire_event.py b/src/utils/management/commands/test_fire_event.py index 10568a12ba..5154c68752 100644 --- a/src/utils/management/commands/test_fire_event.py +++ b/src/utils/management/commands/test_fire_event.py @@ -21,7 +21,7 @@ def create_fake_request(user, journal=None, repository=None): request.FILES = None request.META = {} - request.META = {'REMOTE_ADDR': '127.0.0.1'} + request.META = {"REMOTE_ADDR": "127.0.0.1"} request.model_content_type = None if journal: @@ -47,11 +47,11 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code') - parser.add_argument('user_id') - parser.add_argument('event_name') - parser.add_argument('--json_path', nargs='?') - parser.add_argument('--json_string', nargs='?') + parser.add_argument("journal_code") + parser.add_argument("user_id") + parser.add_argument("event_name") + parser.add_argument("--json_path", nargs="?") + parser.add_argument("--json_string", nargs="?") def handle(self, *args, **options): """ @@ -77,11 +77,11 @@ def handle(self, *args, **options): ] """ - journal_code = options.get('journal_code') - user_id = options.get('user_id') - event_name = options.get('event_name') - json_path = options.get('json_path') - json_string = options.get('json_string', None) + journal_code = options.get("journal_code") + user_id = options.get("user_id") + event_name = options.get("event_name") + json_path = options.get("json_path") + json_string = options.get("json_string", None) user = core_models.Account.objects.get(pk=user_id) try: @@ -92,40 +92,39 @@ def handle(self, *args, **options): request = create_fake_request(user, repository=repository) if not json_path and not json_string: - exit('You must provide a json_path or json_string') + exit("You must provide a json_path or json_string") context = { - 'request': request, + "request": request, } if not json_string: # Check the file exists, exit if not. if not os.path.isfile(json_path): - exit('File does not exist.') + exit("File does not exist.") - file = open(json_path, 'r') + file = open(json_path, "r") json_string = file.read() json_dict = json.loads(json_string) for row in json_dict: - row_type = row.get('type') - context_name = row.get('context_name') + row_type = row.get("type") + context_name = row.get("context_name") - if row_type == 'model': - app = row.get('app') - model = row.get('model') - pk = row.get('pk') + if row_type == "model": + app = row.get("app") + model = row.get("model") + pk = row.get("pk") Model = apps.get_model(app, model) obj = Model.objects.get(pk=pk) context[context_name] = obj - elif row_type == 'variable': - var = row.get('var') + elif row_type == "variable": + var = row.get("var") context[context_name] = var event_logic.Events.raise_event( event_name, **context, ) - diff --git a/src/utils/management/commands/up_fix_article_images.py b/src/utils/management/commands/up_fix_article_images.py index c5c70d9893..553306a23d 100644 --- a/src/utils/management/commands/up_fix_article_images.py +++ b/src/utils/management/commands/up_fix_article_images.py @@ -16,13 +16,13 @@ class Command(BaseCommand): help = "Fixes bad import of XML related image files." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) - parser.add_argument('url') + parser.add_argument("journal_code", nargs="?", default=None) + parser.add_argument("url") def handle(self, *args, **options): """Loops through a journals articles, finds its XML and files and then pulls full versions from UP's site. @@ -31,57 +31,71 @@ def handle(self, *args, **options): :param options: None :return: None """ - journal_code = options.get('journal_code', None) - base_url = options.get('url') + journal_code = options.get("journal_code", None) + base_url = options.get("url") journal = None try: journal = journal_models.Journal.objects.get(code=journal_code) except journal_models.Journal.DoesNotExist: - print('No journal with that code found.') + print("No journal with that code found.") if journal: - articles = models.Article.objects.filter(stage=models.STAGE_PUBLISHED, journal=journal) + articles = models.Article.objects.filter( + stage=models.STAGE_PUBLISHED, journal=journal + ) for article in articles: for galley in article.galley_set.all(): - if galley.label == 'XML' or galley.file.mime_type == 'application/xml' or galley.file.mime_type == 'text/xml': + if ( + galley.label == "XML" + or galley.file.mime_type == "application/xml" + or galley.file.mime_type == "text/xml" + ): missing_supplements = has_missing_supplements(galley) if missing_supplements: - url = '{url}/articles/{doi}'.format(url=base_url, doi=galley.article.identifier.identifier) + url = "{url}/articles/{doi}".format( + url=base_url, doi=galley.article.identifier.identifier + ) r = requests.get(url) - soup = BeautifulSoup(r.text, 'lxml') - main_article = soup.find('div', {'class': 'major-article-block'}) - images = main_article.findAll('img') + soup = BeautifulSoup(r.text, "lxml") + main_article = soup.find( + "div", {"class": "major-article-block"} + ) + images = main_article.findAll("img") galley_content = files.get_file(galley.file, article) - galley_soup = BeautifulSoup(galley_content, 'lxml') - galley_images = galley_soup.findAll('graphic') + galley_soup = BeautifulSoup(galley_content, "lxml") + galley_images = galley_soup.findAll("graphic") - galley_hrefs = [image.get('xlink:href') for image in galley_images] + galley_hrefs = [ + image.get("xlink:href") for image in galley_images + ] print(url) loop_counter = 0 for img in images: - img_url = '{url}/{img_src}'.format(url=url, img_src=img['src']) + img_url = "{url}/{img_src}".format( + url=url, img_src=img["src"] + ) href = galley_hrefs[loop_counter] - href_ids = re.findall('(\\d+)', href) + href_ids = re.findall("(\\d+)", href) file_id = href_ids[1] file = core_models.File.objects.get(id=file_id) - file.privacy = 'public' + file.privacy = "public" file.save() file.unlink_file(article.journal) try: image_r = requests.get(img_url, stream=True) if image_r.status_code == 200: - with open(file.self_article_path(), 'wb+') as f: + with open(file.self_article_path(), "wb+") as f: for chunk in r.iter_content(1024): f.write(chunk) except FileNotFoundError: - print('File not found in files directory.') + print("File not found in files directory.") loop_counter += 1 diff --git a/src/utils/management/commands/up_fix_name_imports.py b/src/utils/management/commands/up_fix_name_imports.py index 1eba7dc3e1..4d847cb00b 100644 --- a/src/utils/management/commands/up_fix_name_imports.py +++ b/src/utils/management/commands/up_fix_name_imports.py @@ -20,60 +20,79 @@ def handle(self, *args, **options): :param options: None :return: None """ - print("This function will search for author and frozen author names with anomalies and allow you to " - "fix them.\n") - translation.activate('en') + print( + "This function will search for author and frozen author names with anomalies and allow you to " + "fix them.\n" + ) + translation.activate("en") - anomaly = input('Insert anomaly to seach for, this will use an i_contains search: ') + anomaly = input( + "Insert anomaly to seach for, this will use an i_contains search: " + ) - print('Searching Account records... ', end='') + print("Searching Account records... ", end="") accounts = models.FrozenAuthor.objects.filter( - Q(first_name__icontains=anomaly) | - Q(middle_name__icontains=anomaly) | - Q(last_name__icontains=anomaly) + Q(first_name__icontains=anomaly) + | Q(middle_name__icontains=anomaly) + | Q(last_name__icontains=anomaly) ) - print('[ok]') - print('Looking through Author objects.') + print("[ok]") + print("Looking through Author objects.") for account in accounts: - - print('Updating {full_name} - {email}'.format(full_name=account.full_name(), - email=account.author.email)) + print( + "Updating {full_name} - {email}".format( + full_name=account.full_name(), email=account.author.email + ) + ) first_update, middle_update, last_update = False, False, False if anomaly in account.first_name: - print('Anomaly detected in first name') + print("Anomaly detected in first name") if not account.first_name == account.author.first_name: - first_update = shared.yes_or_no('Update from the author record ({author_fname})'.format( - author_fname=account.author.first_name)) + first_update = shared.yes_or_no( + "Update from the author record ({author_fname})".format( + author_fname=account.author.first_name + ) + ) if not first_update: - first_update = input('Insert new first name: ') + first_update = input("Insert new first name: ") if anomaly in account.middle_name: - print('Anomaly detected in middle name') + print("Anomaly detected in middle name") if not account.middle_name == account.author.middle_name: - middle_update = shared.yes_or_no('Update from the author record ({author_mname})'.format( - author_mname=account.author.middle_name)) + middle_update = shared.yes_or_no( + "Update from the author record ({author_mname})".format( + author_mname=account.author.middle_name + ) + ) if middle_update: - middle_update = account.author.middle_name if account.author.middle_name else '' - if not middle_update == '' and not middle_update: - middle_update = input('Insert new middle name: ') + middle_update = ( + account.author.middle_name + if account.author.middle_name + else "" + ) + if not middle_update == "" and not middle_update: + middle_update = input("Insert new middle name: ") if anomaly in account.last_name: - print('Anomaly detected in last name') + print("Anomaly detected in last name") if not account.last_name == account.author.last_name: - last_update = shared.yes_or_no('Update from the author record ({author_lname})'.format( - author_lname=account.author.last_name)) + last_update = shared.yes_or_no( + "Update from the author record ({author_lname})".format( + author_lname=account.author.last_name + ) + ) if not last_update: - last_update = input('Insert new last name: ') + last_update = input("Insert new last name: ") if first_update: account.first_name = first_update - if middle_update == '' or middle_update: + if middle_update == "" or middle_update: account.middle_name = middle_update if last_update: diff --git a/src/utils/management/commands/up_import_editing_article.py b/src/utils/management/commands/up_import_editing_article.py index e75e5561a3..de42f958a5 100644 --- a/src/utils/management/commands/up_import_editing_article.py +++ b/src/utils/management/commands/up_import_editing_article.py @@ -9,6 +9,7 @@ class Command(BaseCommand): """ Fetches a backend article from a UP journal """ + help = "Fetches a backend editing article from a UP journal. Requires a journal code, a base url and an article id." def add_arguments(self, parser): @@ -17,10 +18,10 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', default=None) - parser.add_argument('base_url', default=None) - parser.add_argument('auth_file', default=None) - parser.add_argument('--nuke', action='store_true', dest='nuke') + parser.add_argument("journal_code", default=None) + parser.add_argument("base_url", default=None) + parser.add_argument("auth_file", default=None) + parser.add_argument("--nuke", action="store_true", dest="nuke") def handle(self, *args, **options): """Fetches a backend article from UP. @@ -29,15 +30,19 @@ def handle(self, *args, **options): :param options: None :return: None """ - if options.get('nuke'): - management.call_command('nuke_import_cache') + if options.get("nuke"): + management.call_command("nuke_import_cache") - url = '{base_url}/jms/janeway/?request_type=in_editing'.format(base_url=options.get('base_url')) + url = "{base_url}/jms/janeway/?request_type=in_editing".format( + base_url=options.get("base_url") + ) try: - journal = models.Journal.objects.get(code=options.get('journal_code')) - ojs_plugin_import_editing_articles(url, - journal, - auth_file=options.get('auth_file'), - base_url=options.get('base_url')) + journal = models.Journal.objects.get(code=options.get("journal_code")) + ojs_plugin_import_editing_articles( + url, + journal, + auth_file=options.get("auth_file"), + base_url=options.get("base_url"), + ) except models.Journal.DoesNotExist: - print('Journal not found.') + print("Journal not found.") diff --git a/src/utils/management/commands/up_import_review_article.py b/src/utils/management/commands/up_import_review_article.py index c27273096f..540fa48389 100644 --- a/src/utils/management/commands/up_import_review_article.py +++ b/src/utils/management/commands/up_import_review_article.py @@ -9,6 +9,7 @@ class Command(BaseCommand): """ Fetches a backend article from a UP journal """ + help = "Fetches a backend review article from a UP journal. Requires a journal code, a base url and an article id." def add_arguments(self, parser): @@ -17,10 +18,10 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', default=None) - parser.add_argument('base_url', default=None) - parser.add_argument('auth_file', default=None) - parser.add_argument('--nuke', action='store_true', dest='nuke') + parser.add_argument("journal_code", default=None) + parser.add_argument("base_url", default=None) + parser.add_argument("auth_file", default=None) + parser.add_argument("--nuke", action="store_true", dest="nuke") def handle(self, *args, **options): """Fetches a backend article from UP. @@ -29,15 +30,17 @@ def handle(self, *args, **options): :param options: None :return: None """ - if options.get('nuke'): - management.call_command('nuke_import_cache') + if options.get("nuke"): + management.call_command("nuke_import_cache") - url = '{base_url}/jms/janeway/'.format(base_url=options.get('base_url')) + url = "{base_url}/jms/janeway/".format(base_url=options.get("base_url")) try: - journal = models.Journal.objects.get(code=options.get('journal_code')) - ojs_plugin_import_review_articles(url, - journal, - auth_file=options.get('auth_file'), - base_url=options.get('base_url')) + journal = models.Journal.objects.get(code=options.get("journal_code")) + ojs_plugin_import_review_articles( + url, + journal, + auth_file=options.get("auth_file"), + base_url=options.get("base_url"), + ) except models.Journal.DoesNotExist: - print('Journal not found.') + print("Journal not found.") diff --git a/src/utils/management/commands/update_defaults.py b/src/utils/management/commands/update_defaults.py index 74c24411bb..1d32a3ab44 100755 --- a/src/utils/management/commands/update_defaults.py +++ b/src/utils/management/commands/update_defaults.py @@ -13,10 +13,12 @@ def update_default_setting(default_data, db_setting): for default_setting in default_data: - if default_setting['setting'].get('name') == db_setting.setting.name and \ - default_setting['group'].get('name') == db_setting.setting.group.name: - print('Updating {0}'.format(db_setting.setting.name)) - default_setting['value']['default'] = db_setting.value + if ( + default_setting["setting"].get("name") == db_setting.setting.name + and default_setting["group"].get("name") == db_setting.setting.group.name + ): + print("Updating {0}".format(db_setting.setting.name)) + default_setting["value"]["default"] = db_setting.value class Command(BaseCommand): @@ -25,14 +27,14 @@ class Command(BaseCommand): help = "Synchronizes unspecified default settings to all journals." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--journal_code', default=None) - parser.add_argument('--group_name', nargs='?', default=None) - parser.add_argument('--setting_name', nargs='?', default=None) + parser.add_argument("--journal_code", default=None) + parser.add_argument("--group_name", nargs="?", default=None) + parser.add_argument("--setting_name", nargs="?", default=None) def handle(self, *args, **options): """Synchronizes settings to journals. @@ -41,10 +43,10 @@ def handle(self, *args, **options): :param options: None :return: None """ - translation.activate('en') - journal_code = options.get('journal_code', None) - group_name = options.get('group_name', None) - setting_name = options.get('setting_name', None) + translation.activate("en") + journal_code = options.get("journal_code", None) + group_name = options.get("group_name", None) + setting_name = options.get("setting_name", None) if journal_code: journal = journal_models.Journal.objects.get_or(code=journal_code) @@ -52,24 +54,25 @@ def handle(self, *args, **options): journal = None if not group_name and not setting_name: - setting_list = core_models.SettingValue.objects.filter( - journal=journal - ) + setting_list = core_models.SettingValue.objects.filter(journal=journal) if group_name: setting_list = core_models.SettingValue.objects.filter( - setting__group__name=group_name, - journal=journal + setting__group__name=group_name, journal=journal ) if setting_name: setting_list = core_models.SettingValue.objects.filter( setting__group__name=group_name, setting__name=setting_name, - journal=journal + journal=journal, ) - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json'), 'r+', encoding='utf-8') as json_data: + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/journal_defaults.json"), + "r+", + encoding="utf-8", + ) as json_data: default_data = json.load(json_data, object_pairs_hook=OrderedDict) for setting in setting_list: diff --git a/src/utils/management/commands/update_defaults_json.py b/src/utils/management/commands/update_defaults_json.py index f57ced2b8f..1dd1a7fd9d 100755 --- a/src/utils/management/commands/update_defaults_json.py +++ b/src/utils/management/commands/update_defaults_json.py @@ -20,19 +20,27 @@ def handle(self, *args, **options): :param options: None :return: None """ - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json'), - encoding='utf-8') as json_data: + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/journal_defaults.json"), + encoding="utf-8", + ) as json_data: default_data = json.load(json_data) for item in default_data: - - if item['group'].get('name') == 'email': - item['setting']['is_translatable'] = True - elif item['setting'].get('type') == 'rich-text' or item['setting'].get('type') == 'char' or item['setting'].get('type') == 'text'or item['setting'].get('type') == 'json': - item['setting']['is_translatable'] = True + if item["group"].get("name") == "email": + item["setting"]["is_translatable"] = True + elif ( + item["setting"].get("type") == "rich-text" + or item["setting"].get("type") == "char" + or item["setting"].get("type") == "text" + or item["setting"].get("type") == "json" + ): + item["setting"]["is_translatable"] = True else: - item['setting']['is_translatable'] = False + item["setting"]["is_translatable"] = False - write_path = os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json') - with open(write_path, 'w+', encoding="utf-8") as f: + write_path = os.path.join( + settings.BASE_DIR, "utils/install/journal_defaults.json" + ) + with open(write_path, "w+", encoding="utf-8") as f: f.write(json.dumps(default_data, indent=4, sort_keys=True)) diff --git a/src/utils/management/commands/update_emails.py b/src/utils/management/commands/update_emails.py index 4085f06dc2..078322fa1f 100755 --- a/src/utils/management/commands/update_emails.py +++ b/src/utils/management/commands/update_emails.py @@ -11,12 +11,12 @@ class Command(BaseCommand): help = "Synchronizes unspecified default settings to all journals." def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('journal_code', nargs='?', default=None) + parser.add_argument("journal_code", nargs="?", default=None) def handle(self, *args, **options): """Updates email settings for journals. @@ -26,16 +26,16 @@ def handle(self, *args, **options): :return: None """ - translation.activate('en') + translation.activate("en") journals = journal_models.Journal.objects.all() - journal_code = options.get('journal_code', None) + journal_code = options.get("journal_code", None) if journal_code: try: journals = [journal_models.Journal.objects.get(code=journal_code)] except journal_models.Journal.DoesNotExist: journals = None - print('No journal with that code was found.') + print("No journal with that code was found.") if journals: print("Updated emails for {0} Journals:".format(len(journals))) diff --git a/src/utils/management/commands/update_render_galleys.py b/src/utils/management/commands/update_render_galleys.py index b4762b1737..b36edcd96a 100755 --- a/src/utils/management/commands/update_render_galleys.py +++ b/src/utils/management/commands/update_render_galleys.py @@ -12,7 +12,7 @@ class Command(BaseCommand): - """ A management command to update all render galleys from a folder or repo.""" + """A management command to update all render galleys from a folder or repo.""" help = "Update all render galleys from a folder or repo. Dangerous." @@ -22,28 +22,28 @@ def add_arguments(self, parser): :param parser: the parser to which the required arguments will be added :return: None """ - parser.add_argument('--journal_code') - parser.add_argument('--folder_path') + parser.add_argument("--journal_code") + parser.add_argument("--folder_path") def handle(self, *args, **options): - """ Updates all render galleys from a folder. + """Updates all render galleys from a folder. :param args: None :param options: None. :return: None """ - journal_code = options.get('journal_code') - folder_path = options.get('folder_path') + journal_code = options.get("journal_code") + folder_path = options.get("folder_path") try: journal = journal_models.Journal.objects.get(code=journal_code) except journal_models.Journal.DoesNotExist: - print('No journal with that code was found.') + print("No journal with that code was found.") sys.exit() journal_folder = os.path.join(folder_path, journal.code) if not os.path.isdir(journal_folder): - print('No directory found.') + print("No directory found.") sys.exit() article_folders = os.listdir(journal_folder) @@ -51,29 +51,31 @@ def handle(self, *args, **options): for folder in article_folders: try: article = submission_models.Article.objects.get(pk=folder) - print('Article {0} found.'.format(article.pk)) + print("Article {0} found.".format(article.pk)) try: file = os.listdir(os.path.join(journal_folder, folder))[0] - print('File {0} found.'.format(file)) + print("File {0} found.".format(file)) except IndexError: file = None if file: if article.render_galley: - print('Article already has a render galley, updating it.') + print("Article already has a render galley, updating it.") article.render_galley.file.unlink_file() - shutil.copyfile(os.path.join(journal_folder, folder, file), - article.render_galley.file.self_article_path()) - print('Existing render galley updated.') + shutil.copyfile( + os.path.join(journal_folder, folder, file), + article.render_galley.file.self_article_path(), + ) + print("Existing render galley updated.") else: - print('Article does not have a render galley, creating one.') + print("Article does not have a render galley, creating one.") file_object = core_models.File.objects.create( article_id=article.pk, original_filename=file, uuid_filename=uuid.uuid4(), - label='Render Galley', - privacy='public', + label="Render Galley", + privacy="public", mime_type=files.guess_mime(file), ) @@ -81,17 +83,17 @@ def handle(self, *args, **options): article=article, file=file_object, is_remote=False, - label='Render Galley', - type=os.path.splitext(file)[1] + label="Render Galley", + type=os.path.splitext(file)[1], ) article.render_galley = galley_object article.save() - print('New file and galley created.') + print("New file and galley created.") else: - print('No file was found in folder {0}'.format(folder)) + print("No file was found in folder {0}".format(folder)) except submission_models.Article.DoesNotExist: - print('No article found with ID {0}'.format(folder)) + print("No article found with ID {0}".format(folder)) cache.clear() - print('Cache cleared.') + print("Cache cleared.") diff --git a/src/utils/management/commands/xslt_diff.py b/src/utils/management/commands/xslt_diff.py index ab4c96e353..61adf66411 100644 --- a/src/utils/management/commands/xslt_diff.py +++ b/src/utils/management/commands/xslt_diff.py @@ -12,22 +12,20 @@ class Command(BaseCommand): help = "Tests the rendering of an article against the provided XSLFile" def add_arguments(self, parser): - """ Adds arguments to Django's management command-line parser. + """Adds arguments to Django's management command-line parser. :param parser: the parser to which the required arguments will be added :return: None """ parser.add_argument( - 'xslfile_label', - help="The label of the XSLFile being tested" + "xslfile_label", help="The label of the XSLFile being tested" ) parser.add_argument( - 'article_id', - help="The article ID against which the test will be run" + "article_id", help="The article ID against which the test will be run" ) def handle(self, *args, **options): - """ Compares the output + """Compares the output :param args: None :param options: None @@ -35,8 +33,7 @@ def handle(self, *args, **options): """ try: article = models.Article.objects.get(pk=options["article_id"]) - xsl_file = core_models.XSLFile.objects.get( - label=options["xslfile_label"]) + xsl_file = core_models.XSLFile.objects.get(label=options["xslfile_label"]) except models.Article.DoesNotExist: raise CommandError("Couldn't find the article") @@ -48,20 +45,24 @@ def handle(self, *args, **options): ) if xml_galleys.exists(): - if xml_galleys.count() > 1: - print("Found multiple XML galleys for article {id}, " - "returning first match".format(id=article.pk)) + print( + "Found multiple XML galleys for article {id}, " + "returning first match".format(id=article.pk) + ) xml_galley = xml_galleys[0] else: raise CommandError("Article doesn't have an XML Galley") old_render = str(xml_galley.render()) - new_render = str(files.render_xml( - xml_galley.file, article, - xsl_path=xsl_file.file.path, - )) + new_render = str( + files.render_xml( + xml_galley.file, + article, + xsl_path=xsl_file.file.path, + ) + ) diffs = difflib.ndiff(old_render.splitlines(), new_render.splitlines()) total_diffs = 0 @@ -69,5 +70,5 @@ def handle(self, *args, **options): if diff.startswith("-") or diff.startswith("+"): total_diffs += 1 print(diff) - print(80*"=") + print(80 * "=") print("Found %s diffs" % total_diffs) diff --git a/src/utils/middleware.py b/src/utils/middleware.py index f818536994..c223f05597 100755 --- a/src/utils/middleware.py +++ b/src/utils/middleware.py @@ -13,12 +13,12 @@ _local = threading.local() -class BaseMiddleware(): +class BaseMiddleware: def __init__(self, callable): self.get_response = callable def __call__(self, request): - """ Base implementation to ease the transition to Django 3.2 + """Base implementation to ease the transition to Django 3.2 Prior versions of Django used a method called 'process_request'. In this base implementation we maintain that behaviour by calling the older @@ -26,34 +26,34 @@ def __call__(self, request): django documentation for 3.2+ """ - if hasattr(self, 'process_request'): + if hasattr(self, "process_request"): response = self.process_request(request) if response is not None: return response response = self.get_response(request) - if hasattr(self, 'process_response'): + if hasattr(self, "process_response"): self.process_response(request, response) return response class ThemeEngineMiddleware(object): - """ Handles theming through middleware - """ + """Handles theming through middleware""" def process_request(self, request): _local.request = request def process_response(self, request, response): - if hasattr(_local, 'request'): + if hasattr(_local, "request"): del _local.request return response class TimeMonitoring(BaseMiddleware): - """Monitors the resource usage of a request/response cycle """ + """Monitors the resource usage of a request/response cycle""" + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.usage_start = None @@ -71,10 +71,9 @@ def process_response(self, _request, response): @classmethod def _diff_usages(cls, start, end=None): end = end or cls._get_usage() - return tuple(b-a for a,b in zip(start, end)) + return tuple(b - a for a, b in zip(start, end)) @staticmethod def _get_usage(): utime, stime, *_ = resource.getrusage(resource.RUSAGE_THREAD) return (time.time(), utime, stime) - diff --git a/src/utils/migration_utils.py b/src/utils/migration_utils.py index 2efbdde911..c6bee66b76 100644 --- a/src/utils/migration_utils.py +++ b/src/utils/migration_utils.py @@ -9,14 +9,16 @@ from django.db.models import Q -def update_translated_settings(apps, setting_name, group_name, values_to_replace, replacement_value): +def update_translated_settings( + apps, setting_name, group_name, values_to_replace, replacement_value +): """ Deprecated in 1.6, because it only works with an older translation setup that Janeway used to have. Gets a setting then iterates through available languages replacing a list of strings with a replacement string. """ - SettingValue = apps.get_model('core', 'SettingValue') + SettingValue = apps.get_model("core", "SettingValue") languages = [lang[0] for lang in settings.LANGUAGES] setting_values = SettingValue.objects.filter( @@ -26,7 +28,6 @@ def update_translated_settings(apps, setting_name, group_name, values_to_replace for setting_value in setting_values: for language in languages: - with translation.override(language): value = setting_value.value @@ -73,9 +74,9 @@ def replace_strings_in_setting_values( :param languages: A list of language codes to check for old and new strings. """ if not languages: - languages = ['en'] - with translation.override('en'): - SettingValue = apps.get_model('core', 'SettingValue') + languages = ["en"] + with translation.override("en"): + SettingValue = apps.get_model("core", "SettingValue") setting_values = SettingValue.objects.filter( setting__name=setting_name, setting__group__name=group_name, @@ -83,8 +84,8 @@ def replace_strings_in_setting_values( for setting_value in setting_values: for language in languages: - language_var = f'value_{language}' - value = getattr(setting_value, language_var, '') + language_var = f"value_{language}" + value = getattr(setting_value, language_var, "") if not value: continue for old_string, new_string in replacements: @@ -94,13 +95,15 @@ def replace_strings_in_setting_values( setting_value.save() -def update_default_setting_values(apps, setting_name, group_name, values_to_replace, replacement_value): +def update_default_setting_values( + apps, setting_name, group_name, values_to_replace, replacement_value +): """ Updates the specified default setting value where it has not been edited. Accounts for translatable settings that use django-modeltranslation. """ - with translation.override('en'): - SettingValue = apps.get_model('core', 'SettingValue') + with translation.override("en"): + SettingValue = apps.get_model("core", "SettingValue") setting_value = SettingValue.objects.filter( setting__name=setting_name, setting__group__name=group_name, @@ -108,7 +111,7 @@ def update_default_setting_values(apps, setting_name, group_name, values_to_repl ).first() if setting_value: - language_var = "value_{}".format('en') + language_var = "value_{}".format("en") setattr(setting_value, language_var, replacement_value) setting_value.save() @@ -127,7 +130,7 @@ def delete_setting(apps, setting_group_name, setting_name): """ Used to delete a setting in migrations. """ - Setting = apps.get_model('core', 'Setting') + Setting = apps.get_model("core", "Setting") setting = Setting.objects.filter( group__name=setting_group_name, name=setting_name, @@ -146,5 +149,5 @@ def store_empty_strings(model, fields): null=False """ for field in fields: - query = Q((f'{field}__isnull', True)) - model.objects.filter(query).update(**{field: ''}) + query = Q((f"{field}__isnull", True)) + model.objects.filter(query).update(**{field: ""}) diff --git a/src/utils/migrations/0001_initial.py b/src/utils/migrations/0001_initial.py index 1ae85a8982..e26bdafc41 100755 --- a/src/utils/migrations/0001_initial.py +++ b/src/utils/migrations/0001_initial.py @@ -8,98 +8,235 @@ class Migration(migrations.Migration): - initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('journal', '0002_auto_20170711_1203'), - ('contenttypes', '0002_remove_content_type_name'), + ("journal", "0002_auto_20170711_1203"), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.CreateModel( - name='ImportCacheEntry', + name="ImportCacheEntry", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField(max_length=800)), - ('on_disk', models.TextField(max_length=800)), - ('mime_type', models.CharField(blank=True, max_length=200, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("url", models.TextField(max_length=800)), + ("on_disk", models.TextField(max_length=800)), + ("mime_type", models.CharField(blank=True, max_length=200, null=True)), ], ), migrations.CreateModel( - name='LogEntry', + name="LogEntry", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('types', models.CharField(blank=True, choices=[('Email', 'Email'), ('PageView', 'PageView'), ('EditorialAction', 'EditorialAction'), ('Error', 'Error'), ('Authentication', 'Authentication'), ('Submission', 'Submission')], max_length=255, null=True)), - ('date', models.DateTimeField(auto_now_add=True)), - ('description', models.TextField(blank=True, null=True)), - ('level', models.CharField(blank=True, choices=[('Error', 'Error'), ('Debug', 'Debug'), ('Info', 'Info')], max_length=20, null=True)), - ('ip_address', models.GenericIPAddressField(blank=True, null=True)), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('actor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='actor', to=settings.AUTH_USER_MODEL)), - ('content_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='content_type', to='contenttypes.ContentType')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "types", + models.CharField( + blank=True, + choices=[ + ("Email", "Email"), + ("PageView", "PageView"), + ("EditorialAction", "EditorialAction"), + ("Error", "Error"), + ("Authentication", "Authentication"), + ("Submission", "Submission"), + ], + max_length=255, + null=True, + ), + ), + ("date", models.DateTimeField(auto_now_add=True)), + ("description", models.TextField(blank=True, null=True)), + ( + "level", + models.CharField( + blank=True, + choices=[ + ("Error", "Error"), + ("Debug", "Debug"), + ("Info", "Info"), + ], + max_length=20, + null=True, + ), + ), + ("ip_address", models.GenericIPAddressField(blank=True, null=True)), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "actor", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="actor", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "content_type", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="content_type", + to="contenttypes.ContentType", + ), + ), ], options={ - 'verbose_name_plural': 'log entries', + "verbose_name_plural": "log entries", }, ), migrations.CreateModel( - name='Plugin', + name="Plugin", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('version', models.CharField(max_length=10)), - ('date_installed', models.DateTimeField(auto_now_add=True)), - ('enabled', models.BooleanField(default=True)), - ('display_name', models.CharField(blank=True, max_length=200, null=True)), - ('press_wide', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200)), + ("version", models.CharField(max_length=10)), + ("date_installed", models.DateTimeField(auto_now_add=True)), + ("enabled", models.BooleanField(default=True)), + ( + "display_name", + models.CharField(blank=True, max_length=200, null=True), + ), + ("press_wide", models.BooleanField(default=False)), ], ), migrations.CreateModel( - name='PluginSetting', + name="PluginSetting", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('types', models.CharField(choices=[('rich-text', 'Rich Text'), ('text', 'Text'), ('char', 'Characters'), ('number', 'Number'), ('boolean', 'Boolean'), ('file', 'File'), ('select', 'Select'), ('json', 'JSON')], default='Text', max_length=20)), - ('pretty_name', models.CharField(default='', max_length=100)), - ('description', models.TextField(blank=True, null=True)), - ('is_translatable', models.BooleanField(default=False)), - ('plugin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.Plugin')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ( + "types", + models.CharField( + choices=[ + ("rich-text", "Rich Text"), + ("text", "Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), + ], + default="Text", + max_length=20, + ), + ), + ("pretty_name", models.CharField(default="", max_length=100)), + ("description", models.TextField(blank=True, null=True)), + ("is_translatable", models.BooleanField(default=False)), + ( + "plugin", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="utils.Plugin" + ), + ), ], options={ - 'ordering': ('plugin', 'name'), + "ordering": ("plugin", "name"), }, ), migrations.CreateModel( - name='PluginSettingValue', + name="PluginSettingValue", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('journal', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Journal')), - ('setting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.PluginSetting')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "journal", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="journal.Journal", + ), + ), + ( + "setting", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="utils.PluginSetting", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='PluginSettingValueTranslation', + name="PluginSettingValueTranslation", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('value', models.TextField(blank=True, null=True)), - ('language_code', models.CharField(db_index=True, max_length=15)), - ('master', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='utils.PluginSettingValue')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.TextField(blank=True, null=True)), + ("language_code", models.CharField(db_index=True, max_length=15)), + ( + "master", + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="utils.PluginSettingValue", + ), + ), ], options={ - 'db_table': 'utils_pluginsettingvalue_translation', - 'db_tablespace': '', - 'managed': True, - 'abstract': False, - 'default_permissions': (), + "db_table": "utils_pluginsettingvalue_translation", + "db_tablespace": "", + "managed": True, + "abstract": False, + "default_permissions": (), }, ), migrations.AlterUniqueTogether( - name='pluginsettingvaluetranslation', - unique_together=set([('language_code', 'master')]), + name="pluginsettingvaluetranslation", + unique_together=set([("language_code", "master")]), ), ] diff --git a/src/utils/migrations/0002_auto_20170813_1302.py b/src/utils/migrations/0002_auto_20170813_1302.py index f9bd85cdd4..25f581a8cb 100755 --- a/src/utils/migrations/0002_auto_20170813_1302.py +++ b/src/utils/migrations/0002_auto_20170813_1302.py @@ -7,21 +7,20 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0001_initial'), + ("utils", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='pluginsettingvalue', - options={'base_manager_name': '_plain_manager'}, + name="pluginsettingvalue", + options={"base_manager_name": "_plain_manager"}, ), migrations.AlterModelManagers( - name='pluginsettingvalue', + name="pluginsettingvalue", managers=[ - ('objects', django.db.models.manager.Manager()), - ('_plain_manager', django.db.models.manager.Manager()), + ("objects", django.db.models.manager.Manager()), + ("_plain_manager", django.db.models.manager.Manager()), ], ), ] diff --git a/src/utils/migrations/0003_auto_20171113_1251.py b/src/utils/migrations/0003_auto_20171113_1251.py index e03ec7ca99..330335ef90 100755 --- a/src/utils/migrations/0003_auto_20171113_1251.py +++ b/src/utils/migrations/0003_auto_20171113_1251.py @@ -6,35 +6,47 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0002_auto_20170813_1302'), + ("utils", "0002_auto_20170813_1302"), ] operations = [ migrations.AddField( - model_name='logentry', - name='is_email', + model_name="logentry", + name="is_email", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='logentry', - name='message_id', + model_name="logentry", + name="message_id", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='logentry', - name='to', + model_name="logentry", + name="to", field=models.EmailField(blank=True, max_length=254, null=True), ), migrations.AlterField( - model_name='logentry', - name='types', + model_name="logentry", + name="types", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AlterField( - model_name='pluginsetting', - name='types', - field=models.CharField(choices=[('rich-text', 'Rich Text'), ('text', 'Text'), ('char', 'Characters'), ('number', 'Number'), ('boolean', 'Boolean'), ('file', 'File'), ('select', 'Select'), ('json', 'JSON')], default='text', max_length=20), + model_name="pluginsetting", + name="types", + field=models.CharField( + choices=[ + ("rich-text", "Rich Text"), + ("text", "Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), + ], + default="text", + max_length=20, + ), ), ] diff --git a/src/utils/migrations/0004_logentry_subject.py b/src/utils/migrations/0004_logentry_subject.py index 57fdfb0ab8..3362baa929 100755 --- a/src/utils/migrations/0004_logentry_subject.py +++ b/src/utils/migrations/0004_logentry_subject.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0003_auto_20171113_1251'), + ("utils", "0003_auto_20171113_1251"), ] operations = [ migrations.AddField( - model_name='logentry', - name='subject', + model_name="logentry", + name="subject", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/utils/migrations/0005_logentry_message_status.py b/src/utils/migrations/0005_logentry_message_status.py index e083179c8b..c0af93c742 100755 --- a/src/utils/migrations/0005_logentry_message_status.py +++ b/src/utils/migrations/0005_logentry_message_status.py @@ -6,15 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0004_logentry_subject'), + ("utils", "0004_logentry_subject"), ] operations = [ migrations.AddField( - model_name='logentry', - name='message_status', - field=models.CharField(choices=[('no_information', 'No Information'), ('accepted', 'Accepted'), ('delivered', 'Delivered'), ('failed', 'Failed')], default='no_information', max_length=255), + model_name="logentry", + name="message_status", + field=models.CharField( + choices=[ + ("no_information", "No Information"), + ("accepted", "Accepted"), + ("delivered", "Delivered"), + ("failed", "Failed"), + ], + default="no_information", + max_length=255, + ), ), ] diff --git a/src/utils/migrations/0006_auto_20171113_1444.py b/src/utils/migrations/0006_auto_20171113_1444.py index 5cd0a2b52d..8c25a41e94 100755 --- a/src/utils/migrations/0006_auto_20171113_1444.py +++ b/src/utils/migrations/0006_auto_20171113_1444.py @@ -6,20 +6,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0005_logentry_message_status'), + ("utils", "0005_logentry_message_status"), ] operations = [ migrations.AddField( - model_name='logentry', - name='number_status_checks', + model_name="logentry", + name="number_status_checks", field=models.IntegerField(default=0), ), migrations.AddField( - model_name='logentry', - name='status_checks_complete', + model_name="logentry", + name="status_checks_complete", field=models.BooleanField(default=False), ), ] diff --git a/src/utils/migrations/0007_auto_20171215_1051.py b/src/utils/migrations/0007_auto_20171215_1051.py index c804bab959..15188b5ab8 100644 --- a/src/utils/migrations/0007_auto_20171215_1051.py +++ b/src/utils/migrations/0007_auto_20171215_1051.py @@ -7,20 +7,28 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0006_auto_20171113_1444'), + ("utils", "0006_auto_20171113_1444"), ] operations = [ migrations.AddField( - model_name='importcacheentry', - name='date_time', + model_name="importcacheentry", + name="date_time", field=models.DateTimeField(default=django.utils.timezone.now), ), migrations.AlterField( - model_name='logentry', - name='message_status', - field=models.CharField(choices=[('no_information', 'No Information'), ('accepted', 'Sending'), ('delivered', 'Delivered'), ('failed', 'Failed')], default='no_information', max_length=255), + model_name="logentry", + name="message_status", + field=models.CharField( + choices=[ + ("no_information", "No Information"), + ("accepted", "Sending"), + ("delivered", "Delivered"), + ("failed", "Failed"), + ], + default="no_information", + max_length=255, + ), ), ] diff --git a/src/utils/migrations/0008_version.py b/src/utils/migrations/0008_version.py index 99f24e386a..7aa61c1dda 100644 --- a/src/utils/migrations/0008_version.py +++ b/src/utils/migrations/0008_version.py @@ -7,18 +7,25 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0007_auto_20171215_1051'), + ("utils", "0007_auto_20171215_1051"), ] operations = [ migrations.CreateModel( - name='Version', + name="Version", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('number', models.CharField(max_length=5)), - ('date', models.DateTimeField(default=django.utils.timezone.now)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("number", models.CharField(max_length=5)), + ("date", models.DateTimeField(default=django.utils.timezone.now)), ], ), ] diff --git a/src/utils/migrations/0009_auto_20180808_1514.py b/src/utils/migrations/0009_auto_20180808_1514.py index 41efdfb6f1..89c147cfa1 100644 --- a/src/utils/migrations/0009_auto_20180808_1514.py +++ b/src/utils/migrations/0009_auto_20180808_1514.py @@ -6,12 +6,12 @@ def create_version(apps, schema_editor): - Version = apps.get_model('utils', 'Version') + Version = apps.get_model("utils", "Version") versions = Version.objects.all() if not versions: - Version.objects.create(number='1.2') + Version.objects.create(number="1.2") def delete_version(apps, schema_editor): @@ -19,11 +19,8 @@ def delete_version(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('utils', '0008_version'), + ("utils", "0008_version"), ] - operations = [ - migrations.RunPython(create_version, reverse_code=delete_version) - ] + operations = [migrations.RunPython(create_version, reverse_code=delete_version)] diff --git a/src/utils/migrations/0010_version_rollback.py b/src/utils/migrations/0010_version_rollback.py index bdd1becd18..bd7c816539 100644 --- a/src/utils/migrations/0010_version_rollback.py +++ b/src/utils/migrations/0010_version_rollback.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0009_auto_20180808_1514'), + ("utils", "0009_auto_20180808_1514"), ] operations = [ migrations.AddField( - model_name='version', - name='rollback', + model_name="version", + name="rollback", field=models.DateTimeField(blank=True, null=True), ), ] diff --git a/src/utils/migrations/0011_upgrade_1_3_5.py b/src/utils/migrations/0011_upgrade_1_3_5.py index 1b718f0180..5f91c148ad 100644 --- a/src/utils/migrations/0011_upgrade_1_3_5.py +++ b/src/utils/migrations/0011_upgrade_1_3_5.py @@ -4,21 +4,23 @@ VERSION = "1.3.5" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0010_version_rollback'), + ("utils", "0010_version_rollback"), ] operations = [ diff --git a/src/utils/migrations/0012_plugin_homepage_element.py b/src/utils/migrations/0012_plugin_homepage_element.py index 478fe6fe74..9626be704f 100644 --- a/src/utils/migrations/0012_plugin_homepage_element.py +++ b/src/utils/migrations/0012_plugin_homepage_element.py @@ -7,26 +7,27 @@ def fix_plugins(apps, schema_editor): plugin_names = [ - 'HTML', - 'News', + "HTML", + "News", ] - Plugin = apps.get_model('utils', 'Plugin') + Plugin = apps.get_model("utils", "Plugin") plugins = Plugin.objects.filter(name__in=plugin_names).update( homepage_element=True, ) class Migration(migrations.Migration): - dependencies = [ - ('utils', '0011_upgrade_1_3_5'), + ("utils", "0011_upgrade_1_3_5"), ] operations = [ migrations.AddField( - model_name='plugin', - name='homepage_element', - field=models.BooleanField(default=False, help_text='Enable if the plugin is a homepage element.'), + model_name="plugin", + name="homepage_element", + field=models.BooleanField( + default=False, help_text="Enable if the plugin is a homepage element." + ), ), migrations.RunPython( fix_plugins, diff --git a/src/utils/migrations/0013_upgrade_1_3_6.py b/src/utils/migrations/0013_upgrade_1_3_6.py index 716a6a9449..990fdd929f 100644 --- a/src/utils/migrations/0013_upgrade_1_3_6.py +++ b/src/utils/migrations/0013_upgrade_1_3_6.py @@ -4,22 +4,24 @@ VERSION = "1.3.6" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0012_plugin_homepage_element'), - ('core', '0030_merge_20190405_1549'), + ("utils", "0012_plugin_homepage_element"), + ("core", "0030_merge_20190405_1549"), ] operations = [ diff --git a/src/utils/migrations/0014_upgrade_1_3_7.py b/src/utils/migrations/0014_upgrade_1_3_7.py index 954c6517ef..31a0f0cf55 100644 --- a/src/utils/migrations/0014_upgrade_1_3_7.py +++ b/src/utils/migrations/0014_upgrade_1_3_7.py @@ -4,22 +4,24 @@ VERSION = "1.3.7" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0013_upgrade_1_3_6'), - ('core', '0033_set_default_xml_galley_xsl'), + ("utils", "0013_upgrade_1_3_6"), + ("core", "0033_set_default_xml_galley_xsl"), ] operations = [ diff --git a/src/utils/migrations/0015_upgrade_1_3_8.py b/src/utils/migrations/0015_upgrade_1_3_8.py index 39cfe9fb23..6c08a2c917 100644 --- a/src/utils/migrations/0015_upgrade_1_3_8.py +++ b/src/utils/migrations/0015_upgrade_1_3_8.py @@ -4,22 +4,24 @@ VERSION = "1.3.8" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0014_upgrade_1_3_7'), - ('core', '0040_auto_20200529_1415'), + ("utils", "0014_upgrade_1_3_7"), + ("core", "0040_auto_20200529_1415"), ] operations = [ diff --git a/src/utils/migrations/0016_auto_20200721_1419.py b/src/utils/migrations/0016_auto_20200721_1419.py index 9288802f67..d19627b1f4 100644 --- a/src/utils/migrations/0016_auto_20200721_1419.py +++ b/src/utils/migrations/0016_auto_20200721_1419.py @@ -7,22 +7,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0015_upgrade_1_3_8'), + ("utils", "0015_upgrade_1_3_8"), ] operations = [ migrations.CreateModel( - name='ToAddress', + name="ToAddress", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('email', models.EmailField(max_length=300)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("email", models.EmailField(max_length=300)), ], ), migrations.AddField( - model_name='toaddress', - name='log_entry', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.LogEntry'), + model_name="toaddress", + name="log_entry", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="utils.LogEntry" + ), ), ] diff --git a/src/utils/migrations/0017_auto_20200721_1421.py b/src/utils/migrations/0017_auto_20200721_1421.py index 154cbb0969..800d4d80a9 100644 --- a/src/utils/migrations/0017_auto_20200721_1421.py +++ b/src/utils/migrations/0017_auto_20200721_1421.py @@ -5,14 +5,15 @@ from django.db import migrations, transaction + def parse_email_addresses(entry): - match = re.findall(r'[\w\.\+-]+@[\w\.-]+\.\w+', entry.to) + match = re.findall(r"[\w\.\+-]+@[\w\.-]+\.\w+", entry.to) return match def move_to_m2m(apps, schema_editor): - LogEntry = apps.get_model('utils', 'LogEntry') - ToAddress = apps.get_model('utils', 'ToAddress') + LogEntry = apps.get_model("utils", "LogEntry") + ToAddress = apps.get_model("utils", "ToAddress") email_log_entries = LogEntry.objects.filter(to__isnull=False) @@ -26,7 +27,7 @@ def move_to_m2m(apps, schema_editor): def move_to_string(apps, schema_editor): - LogEntry = apps.get_model('utils', 'LogEntry') + LogEntry = apps.get_model("utils", "LogEntry") all_log_entries = LogEntry.objects.all() @@ -38,9 +39,8 @@ def move_to_string(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('utils', '0016_auto_20200721_1419'), + ("utils", "0016_auto_20200721_1419"), ] operations = [ diff --git a/src/utils/migrations/0018_remove_logentry_to.py b/src/utils/migrations/0018_remove_logentry_to.py index 9bb03d02e9..319f347ee3 100644 --- a/src/utils/migrations/0018_remove_logentry_to.py +++ b/src/utils/migrations/0018_remove_logentry_to.py @@ -6,14 +6,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0017_auto_20200721_1421'), + ("utils", "0017_auto_20200721_1421"), ] operations = [ migrations.RemoveField( - model_name='logentry', - name='to', + model_name="logentry", + name="to", ), ] diff --git a/src/utils/migrations/0019_upgrade_1_3_9.py b/src/utils/migrations/0019_upgrade_1_3_9.py index 9a9f33be4d..435482df30 100644 --- a/src/utils/migrations/0019_upgrade_1_3_9.py +++ b/src/utils/migrations/0019_upgrade_1_3_9.py @@ -4,22 +4,24 @@ VERSION = "1.3.9" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0018_remove_logentry_to'), - ('core', '0042_auto_20200825_1051'), + ("utils", "0018_remove_logentry_to"), + ("core", "0042_auto_20200825_1051"), ] operations = [ diff --git a/src/utils/migrations/0020_auto_20210222_1750.py b/src/utils/migrations/0020_auto_20210222_1750.py index ac2a64b6fb..599d5a56fe 100644 --- a/src/utils/migrations/0020_auto_20210222_1750.py +++ b/src/utils/migrations/0020_auto_20210222_1750.py @@ -8,11 +8,13 @@ def migrate_plugin_settings(apps, schema_editor): - PluginSettingValue = apps.get_model('utils', 'PluginSettingValue') - PluginSettingValueTranslation = apps.get_model('utils', 'PluginSettingValueTranslation') - SettingGroup = apps.get_model('core', 'SettingGroup') - Setting = apps.get_model('core', 'Setting') - SettingValue = apps.get_model('core', 'SettingValue') + PluginSettingValue = apps.get_model("utils", "PluginSettingValue") + PluginSettingValueTranslation = apps.get_model( + "utils", "PluginSettingValueTranslation" + ) + SettingGroup = apps.get_model("core", "SettingGroup") + Setting = apps.get_model("core", "Setting") + SettingValue = apps.get_model("core", "SettingValue") translations = PluginSettingValueTranslation.objects.all() @@ -22,14 +24,14 @@ def migrate_plugin_settings(apps, schema_editor): ) plugin_setting = plugin_setting_value.setting - plugin_group_name = 'plugin:{plugin_name}'.format( + plugin_group_name = "plugin:{plugin_name}".format( plugin_name=plugin_setting.plugin.name, ) setting_group, c = SettingGroup.objects.get_or_create( name=plugin_group_name, defaults={ - 'enabled': True, - } + "enabled": True, + }, ) setting, c = Setting.objects.get_or_create( name=plugin_setting.name, @@ -40,7 +42,9 @@ def migrate_plugin_settings(apps, schema_editor): ) with _translation.override(settings.LANGUAGE_CODE): setting_value, c = SettingValue.objects.get_or_create( - journal=plugin_setting_value.journal if plugin_setting_value.journal else None, + journal=plugin_setting_value.journal + if plugin_setting_value.journal + else None, setting=setting, value=translation.value, ) @@ -54,11 +58,12 @@ def migrate_plugin_settings(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('utils', '0019_upgrade_1_3_9'), + ("utils", "0019_upgrade_1_3_9"), ] operations = [ - migrations.RunPython(migrate_plugin_settings, reverse_code=migrations.RunPython.noop), + migrations.RunPython( + migrate_plugin_settings, reverse_code=migrations.RunPython.noop + ), ] diff --git a/src/utils/migrations/0020_upgrade_1_3_10.py b/src/utils/migrations/0020_upgrade_1_3_10.py index 6018ba02fc..5556c0875c 100644 --- a/src/utils/migrations/0020_upgrade_1_3_10.py +++ b/src/utils/migrations/0020_upgrade_1_3_10.py @@ -5,28 +5,30 @@ VERSION = "1.3.10" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0019_upgrade_1_3_9'), - ('core', '0050_xslt_1-3-10'), + ("utils", "0019_upgrade_1_3_9"), + ("core", "0050_xslt_1-3-10"), ] operations = [ migrations.AlterField( - model_name='version', - name='number', + model_name="version", + name="number", field=models.CharField(max_length=10), ), migrations.RunPython(upgrade, reverse_code=rollback), diff --git a/src/utils/migrations/0021_auto_20210222_1903.py b/src/utils/migrations/0021_auto_20210222_1903.py index 38571ca125..3b78c84137 100644 --- a/src/utils/migrations/0021_auto_20210222_1903.py +++ b/src/utils/migrations/0021_auto_20210222_1903.py @@ -6,39 +6,38 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0020_auto_20210222_1750'), + ("utils", "0020_auto_20210222_1750"), ] operations = [ migrations.RemoveField( - model_name='pluginsetting', - name='plugin', + model_name="pluginsetting", + name="plugin", ), migrations.RemoveField( - model_name='pluginsettingvalue', - name='journal', + model_name="pluginsettingvalue", + name="journal", ), migrations.RemoveField( - model_name='pluginsettingvalue', - name='setting', + model_name="pluginsettingvalue", + name="setting", ), migrations.AlterUniqueTogether( - name='pluginsettingvaluetranslation', + name="pluginsettingvaluetranslation", unique_together=set([]), ), migrations.RemoveField( - model_name='pluginsettingvaluetranslation', - name='master', + model_name="pluginsettingvaluetranslation", + name="master", ), migrations.DeleteModel( - name='PluginSetting', + name="PluginSetting", ), migrations.DeleteModel( - name='PluginSettingValue', + name="PluginSettingValue", ), migrations.DeleteModel( - name='PluginSettingValueTranslation', + name="PluginSettingValueTranslation", ), ] diff --git a/src/utils/migrations/0022_merge_20210624_0923.py b/src/utils/migrations/0022_merge_20210624_0923.py index 93368ceaa8..1e4e233fd8 100644 --- a/src/utils/migrations/0022_merge_20210624_0923.py +++ b/src/utils/migrations/0022_merge_20210624_0923.py @@ -6,11 +6,9 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0020_upgrade_1_3_10'), - ('utils', '0021_auto_20210222_1903'), + ("utils", "0020_upgrade_1_3_10"), + ("utils", "0021_auto_20210222_1903"), ] - operations = [ - ] + operations = [] diff --git a/src/utils/migrations/0023_upgrade_1_4_0.py b/src/utils/migrations/0023_upgrade_1_4_0.py index e3965d46bb..33c3bee783 100644 --- a/src/utils/migrations/0023_upgrade_1_4_0.py +++ b/src/utils/migrations/0023_upgrade_1_4_0.py @@ -5,28 +5,30 @@ VERSION = "1.4.0" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0022_merge_20210624_0923'), - ('core', '0057_xslt_1-4-0'), + ("utils", "0022_merge_20210624_0923"), + ("core", "0057_xslt_1-4-0"), ] operations = [ migrations.AlterField( - model_name='version', - name='number', + model_name="version", + name="number", field=models.CharField(max_length=10), ), migrations.RunPython(upgrade, reverse_code=rollback), diff --git a/src/utils/migrations/0024_auto_20211119_1015.py b/src/utils/migrations/0024_auto_20211119_1015.py index a3dcc6f25b..5d5c0cb56c 100644 --- a/src/utils/migrations/0024_auto_20211119_1015.py +++ b/src/utils/migrations/0024_auto_20211119_1015.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0023_upgrade_1_4_0'), + ("utils", "0023_upgrade_1_4_0"), ] operations = [ migrations.AddField( - model_name='logentry', - name='email_subject', + model_name="logentry", + name="email_subject", field=models.TextField(blank=True, null=True), ), ] diff --git a/src/utils/migrations/0025_upgrade_1_4_1.py b/src/utils/migrations/0025_upgrade_1_4_1.py index 1a2e5ffcb4..40665e913f 100644 --- a/src/utils/migrations/0025_upgrade_1_4_1.py +++ b/src/utils/migrations/0025_upgrade_1_4_1.py @@ -5,22 +5,24 @@ VERSION = "1.4.1" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0024_auto_20211119_1015'), - ('core', '0068_xslt_1-4-1'), + ("utils", "0024_auto_20211119_1015"), + ("core", "0068_xslt_1-4-1"), ] operations = [ diff --git a/src/utils/migrations/0026_upgrade_1_4_2.py b/src/utils/migrations/0026_upgrade_1_4_2.py index 2d87a140c5..8384ead94e 100644 --- a/src/utils/migrations/0026_upgrade_1_4_2.py +++ b/src/utils/migrations/0026_upgrade_1_4_2.py @@ -5,22 +5,24 @@ VERSION = "1.4.2" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0025_upgrade_1_4_1'), - ('core', '0070_auto_20220506_1652'), + ("utils", "0025_upgrade_1_4_1"), + ("core", "0070_auto_20220506_1652"), ] operations = [ diff --git a/src/utils/migrations/0027_upgrade_1_4_3.py b/src/utils/migrations/0027_upgrade_1_4_3.py index d1e657dc3f..e28888fc56 100644 --- a/src/utils/migrations/0027_upgrade_1_4_3.py +++ b/src/utils/migrations/0027_upgrade_1_4_3.py @@ -5,22 +5,24 @@ VERSION = "1.4.3" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0026_upgrade_1_4_2'), - ('core', '0075_xslt_1-4-3'), + ("utils", "0026_upgrade_1_4_2"), + ("core", "0075_xslt_1-4-3"), ] operations = [ diff --git a/src/utils/migrations/0028_upgrade_1_4_4.py b/src/utils/migrations/0028_upgrade_1_4_4.py index 6d25183784..89d04b496d 100644 --- a/src/utils/migrations/0028_upgrade_1_4_4.py +++ b/src/utils/migrations/0028_upgrade_1_4_4.py @@ -5,22 +5,24 @@ VERSION = "1.4.4" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0027_upgrade_1_4_3'), - ('core', '0078_anonymous_review_rewording'), + ("utils", "0027_upgrade_1_4_3"), + ("core", "0078_anonymous_review_rewording"), ] operations = [ diff --git a/src/utils/migrations/0029_alter_toaddress_options.py b/src/utils/migrations/0029_alter_toaddress_options.py index 13fab22feb..c47e59029f 100644 --- a/src/utils/migrations/0029_alter_toaddress_options.py +++ b/src/utils/migrations/0029_alter_toaddress_options.py @@ -4,14 +4,13 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0028_upgrade_1_4_4'), + ("utils", "0028_upgrade_1_4_4"), ] operations = [ migrations.AlterModelOptions( - name='toaddress', - options={'verbose_name_plural': 'to addresses'}, + name="toaddress", + options={"verbose_name_plural": "to addresses"}, ), ] diff --git a/src/utils/migrations/0030_upgrade_1_5_0.py b/src/utils/migrations/0030_upgrade_1_5_0.py index c28c46c4c5..6743daec29 100644 --- a/src/utils/migrations/0030_upgrade_1_5_0.py +++ b/src/utils/migrations/0030_upgrade_1_5_0.py @@ -5,22 +5,24 @@ VERSION = "1.5.0" + def rollback(apps, schema_editor): version_model = apps.get_model("utils", "Version") latest_version = version_model.objects.get(number=VERSION, rollback=None) latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0029_alter_toaddress_options'), - ('core', '0080_merge_20230317_1530'), + ("utils", "0029_alter_toaddress_options"), + ("core", "0080_merge_20230317_1530"), ] operations = [ diff --git a/src/utils/migrations/0031_upgrade_1_5_1.py b/src/utils/migrations/0031_upgrade_1_5_1.py index 535c885b84..286c8b81d1 100644 --- a/src/utils/migrations/0031_upgrade_1_5_1.py +++ b/src/utils/migrations/0031_upgrade_1_5_1.py @@ -12,16 +12,17 @@ def rollback(apps, schema_editor): latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0030_upgrade_1_5_0'), - ('core', '0084_xslt_1-5-1'), + ("utils", "0030_upgrade_1_5_0"), + ("core", "0084_xslt_1-5-1"), ] operations = [ diff --git a/src/utils/migrations/0032_upgrade_1_5_2.py b/src/utils/migrations/0032_upgrade_1_5_2.py index d30047b08f..760d21fcc7 100644 --- a/src/utils/migrations/0032_upgrade_1_5_2.py +++ b/src/utils/migrations/0032_upgrade_1_5_2.py @@ -12,16 +12,17 @@ def rollback(apps, schema_editor): latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0031_upgrade_1_5_1'), - ('core', '0087_merge_20240315_1649'), + ("utils", "0031_upgrade_1_5_1"), + ("core", "0087_merge_20240315_1649"), ] operations = [ diff --git a/src/utils/migrations/0033_upgrade_1_6_0.py b/src/utils/migrations/0033_upgrade_1_6_0.py index 608a9cb6e2..13cf046fe6 100644 --- a/src/utils/migrations/0033_upgrade_1_6_0.py +++ b/src/utils/migrations/0033_upgrade_1_6_0.py @@ -12,16 +12,17 @@ def rollback(apps, schema_editor): latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0032_upgrade_1_5_2'), - ('core', '0092_xslt_1-6-0'), + ("utils", "0032_upgrade_1_5_2"), + ("core", "0092_xslt_1-6-0"), ] operations = [ diff --git a/src/utils/migrations/0034_rename_toaddress_addressee.py b/src/utils/migrations/0034_rename_toaddress_addressee.py index 3341b73902..2fb2a6e33c 100644 --- a/src/utils/migrations/0034_rename_toaddress_addressee.py +++ b/src/utils/migrations/0034_rename_toaddress_addressee.py @@ -4,23 +4,26 @@ class Migration(migrations.Migration): - dependencies = [ - ('utils', '0033_upgrade_1_6_0'), + ("utils", "0033_upgrade_1_6_0"), ] operations = [ migrations.RenameModel( - old_name='ToAddress', - new_name='Addressee', + old_name="ToAddress", + new_name="Addressee", ), migrations.AlterModelOptions( - name='addressee', + name="addressee", options={}, ), migrations.AddField( - model_name='addressee', - name='field', - field=models.CharField(blank=True, choices=[('to', 'To'), ('cc', 'CC'), ('bcc', 'BCC')], max_length=3), + model_name="addressee", + name="field", + field=models.CharField( + blank=True, + choices=[("to", "To"), ("cc", "CC"), ("bcc", "BCC")], + max_length=3, + ), ), ] diff --git a/src/utils/migrations/0035_upgrade_1_7_0.py b/src/utils/migrations/0035_upgrade_1_7_0.py index 2a372e6f80..431da3469a 100644 --- a/src/utils/migrations/0035_upgrade_1_7_0.py +++ b/src/utils/migrations/0035_upgrade_1_7_0.py @@ -12,16 +12,17 @@ def rollback(apps, schema_editor): latest_version.rollback = timezone.now() latest_version.save() + def upgrade(apps, schema_editor): version_model = apps.get_model("utils", "Version") new_version = version_model.objects.create(number=VERSION) new_version.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('utils', '0034_rename_toaddress_addressee'), - ('core', '0097_merge_20240819_1617'), + ("utils", "0034_rename_toaddress_addressee"), + ("core", "0097_merge_20240819_1617"), ] operations = [ diff --git a/src/utils/migrations/0036_typesetting_guide.py b/src/utils/migrations/0036_typesetting_guide.py index 220ed6d773..e666d3b327 100644 --- a/src/utils/migrations/0036_typesetting_guide.py +++ b/src/utils/migrations/0036_typesetting_guide.py @@ -4,24 +4,23 @@ def update_typesetting_guide(apps, schema_editor): values_to_replace = [ - 'Here are some guidelines.', + "Here are some guidelines.", ] - replacement_value = '

When uploading galleys, please label them by the format, such as “XML” or “PDF”.

XML and HTML galleys can be previewed after uploading to check how they will be rendered when published.

The Galleys section should only contain one representative file for each format. If you are uploading the first version of a new format, please upload a new Galley (typeset file). If you are making revisions or corrections to an existing format, please use select Edit and then upload the new version, rather than adding a new Galley.

' + replacement_value = "

When uploading galleys, please label them by the format, such as “XML” or “PDF”.

XML and HTML galleys can be previewed after uploading to check how they will be rendered when published.

The Galleys section should only contain one representative file for each format. If you are uploading the first version of a new format, please upload a new Galley (typeset file). If you are making revisions or corrections to an existing format, please use select Edit and then upload the new version, rather than adding a new Galley.

" migration_utils.update_default_setting_values( apps, - setting_name='typesetting_guide', - group_name='general', + setting_name="typesetting_guide", + group_name="general", values_to_replace=values_to_replace, replacement_value=replacement_value, ) class Migration(migrations.Migration): - dependencies = [ - ('utils', '0035_upgrade_1_7_0'), + ("utils", "0035_upgrade_1_7_0"), ] operations = [ diff --git a/src/utils/migrations/0037_upgrade_1_7_1.py b/src/utils/migrations/0037_upgrade_1_7_1.py index 52742aa322..55df2c6b95 100644 --- a/src/utils/migrations/0037_upgrade_1_7_1.py +++ b/src/utils/migrations/0037_upgrade_1_7_1.py @@ -20,9 +20,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('utils', '0036_typesetting_guide'), + ("utils", "0036_typesetting_guide"), ] operations = [ diff --git a/src/utils/migrations/0038_upgrade_1_7_2.py b/src/utils/migrations/0038_upgrade_1_7_2.py index d0e259ef8e..a6f2955606 100644 --- a/src/utils/migrations/0038_upgrade_1_7_2.py +++ b/src/utils/migrations/0038_upgrade_1_7_2.py @@ -20,9 +20,8 @@ def upgrade(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('utils', '0037_upgrade_1_7_1'), + ("utils", "0037_upgrade_1_7_1"), ] operations = [ diff --git a/src/utils/models.py b/src/utils/models.py index 0ad6918794..72542d8ea2 100755 --- a/src/utils/models.py +++ b/src/utils/models.py @@ -21,75 +21,90 @@ LOG_TYPES = [ - ('Email', 'Email'), - ('PageView', 'PageView'), - ('EditorialAction', 'EditorialAction'), - ('Error', 'Error'), - ('Authentication', 'Authentication'), - ('Submission', 'Submission'), - ('Publication', 'Publication') + ("Email", "Email"), + ("PageView", "PageView"), + ("EditorialAction", "EditorialAction"), + ("Error", "Error"), + ("Authentication", "Authentication"), + ("Submission", "Submission"), + ("Publication", "Publication"), ] LOG_LEVELS = [ - ('Error', 'Error'), - ('Debug', 'Debug'), - ('Info', 'Info'), + ("Error", "Error"), + ("Debug", "Debug"), + ("Info", "Info"), ] MESSAGE_STATUS = [ - ('no_information', 'No Information'), - ('accepted', 'Sending'), - ('delivered', 'Delivered'), - ('failed', 'Failed'), + ("no_information", "No Information"), + ("accepted", "Sending"), + ("delivered", "Delivered"), + ("failed", "Failed"), ] EMAIL_RECIPIENT_FIELDS = [ - ('to', 'To'), - ('cc', 'CC'), - ('bcc', 'BCC'), + ("to", "To"), + ("cc", "CC"), + ("bcc", "BCC"), ] + class LogEntry(models.Model): types = models.CharField(max_length=255, null=True, blank=True) date = models.DateTimeField(auto_now_add=True) subject = models.TextField(null=True, blank=True) description = models.TextField(null=True, blank=True) level = models.CharField(max_length=20, null=True, blank=True, choices=LOG_LEVELS) - actor = models.ForeignKey('core.Account', null=True, blank=True, related_name='actor', on_delete=models.SET_NULL) + actor = models.ForeignKey( + "core.Account", + null=True, + blank=True, + related_name="actor", + on_delete=models.SET_NULL, + ) ip_address = models.GenericIPAddressField(null=True, blank=True) - content_type = models.ForeignKey(ContentType, on_delete=models.SET_NULL, related_name='content_type', null=True) + content_type = models.ForeignKey( + ContentType, on_delete=models.SET_NULL, related_name="content_type", null=True + ) object_id = models.PositiveIntegerField(blank=True, null=True) - target = GenericForeignKey('content_type', 'object_id') + target = GenericForeignKey("content_type", "object_id") is_email = models.BooleanField(default=False) email_subject = models.TextField(blank=True, null=True) message_id = models.TextField(blank=True, null=True) - message_status = models.CharField(max_length=255, choices=MESSAGE_STATUS, default='no_information') + message_status = models.CharField( + max_length=255, choices=MESSAGE_STATUS, default="no_information" + ) number_status_checks = models.IntegerField(default=0) status_checks_complete = models.BooleanField(default=False) class Meta: - verbose_name_plural = 'log entries' + verbose_name_plural = "log entries" def __str__(self): - return u'[{0}] {1} - {2} {3}'.format(self.types, self.date, self.subject, self.message_id) + return "[{0}] {1} - {2} {3}".format( + self.types, self.date, self.subject, self.message_id + ) def __repr__(self): - return u'[{0}] {1} - {2} {3}'.format(self.types, self.date, self.subject, self.message_id) + return "[{0}] {1} - {2} {3}".format( + self.types, self.date, self.subject, self.message_id + ) def message_status_class(self): - if self.message_status == 'delivered': - return 'green' - elif self.message_status == 'failed': - return 'red' + if self.message_status == "delivered": + return "green" + elif self.message_status == "failed": + return "red" else: - return 'amber' + return "amber" @property def to(self): - """ Deprecated in 1.6 because of ambiguity with cc and bcc fields. - Use addressee_emails instead. + """Deprecated in 1.6 because of ambiguity with cc and bcc fields. + Use addressee_emails instead. """ return self.addressee_emails @@ -103,48 +118,45 @@ def addressee_emails(self): @staticmethod def add_entry( - types, - description, - level, - actor=None, - request=None, - target=None, - is_email=False, - to=None, - message_id=None, - subject=None, - email_subject=None, - cc=None, - bcc=None + types, + description, + level, + actor=None, + request=None, + target=None, + is_email=False, + to=None, + message_id=None, + subject=None, + email_subject=None, + cc=None, + bcc=None, ): - # When a user is not logged in request.user is a SimpleLazyObject # so we check if the actor is_anonymous. if actor is not None and actor.is_anonymous: actor = None kwargs = { - 'types': types, - 'description': description, - 'level': level, + "types": types, + "description": description, + "level": level, # if no actor is supplied, assume anonymous - 'actor': actor if actor else None, - 'ip_address': get_ip_address(request), - 'target': target, - 'is_email': is_email, - 'message_id': message_id, - 'subject': subject, - 'email_subject': email_subject, + "actor": actor if actor else None, + "ip_address": get_ip_address(request), + "target": target, + "is_email": is_email, + "message_id": message_id, + "subject": subject, + "email_subject": email_subject, } new_entry = LogEntry.objects.create(**kwargs) - for emails, field in [(to, 'to'), (cc, 'cc'), (bcc, 'bcc')]: + for emails, field in [(to, "to"), (cc, "cc"), (bcc, "bcc")]: if emails: for email in emails: Addressee.objects.create( - log_entry=new_entry, - email=email, - field=field + log_entry=new_entry, email=email, field=field ) return new_entry @@ -190,7 +202,9 @@ class Version(models.Model): rollback = models.DateTimeField(blank=True, null=True) def __str__(self): - return 'Version {number}, upgraded {date}'.format(number=self.number, date=self.date) + return "Version {number}, upgraded {date}".format( + number=self.number, date=self.date + ) class Plugin(models.Model): @@ -201,15 +215,14 @@ class Plugin(models.Model): display_name = models.CharField(max_length=200, blank=True, null=True) press_wide = models.BooleanField(default=False) homepage_element = models.BooleanField( - default=False, - help_text='Enable if the plugin is a homepage element.' + default=False, help_text="Enable if the plugin is a homepage element." ) def __str__(self): - return u'[{0}] {1} - {2}'.format(self.name, self.version, self.enabled) + return "[{0}] {1} - {2}".format(self.name, self.version, self.enabled) def __repr__(self): - return u'[{0}] {1} - {2}'.format(self.name, self.version, self.enabled) + return "[{0}] {1} - {2}".format(self.name, self.version, self.enabled) def best_name(self, slug=False): if self.display_name: @@ -224,15 +237,15 @@ def best_name(self, slug=False): setting_types = ( - ('rich-text', 'Rich Text'), - ('mini-html', 'Mini HTML'), - ('text', 'Plain Text'), - ('char', 'Characters'), - ('number', 'Number'), - ('boolean', 'Boolean'), - ('file', 'File'), - ('select', 'Select'), - ('json', 'JSON'), + ("rich-text", "Rich Text"), + ("mini-html", "Mini HTML"), + ("text", "Plain Text"), + ("char", "Characters"), + ("number", "Number"), + ("boolean", "Boolean"), + ("file", "File"), + ("select", "Select"), + ("json", "JSON"), ) @@ -255,7 +268,7 @@ def delete(self, *args, **kwargs): super().delete(*args, **kwargs) @staticmethod - def fetch(url, up_auth_file='', up_base_url='', ojs_auth_file=''): + def fetch(url, up_auth_file="", up_base_url="", ojs_auth_file=""): try: cached = ImportCacheEntry.objects.get(url=url) @@ -271,7 +284,7 @@ def fetch(url, up_auth_file='', up_base_url='', ojs_auth_file=''): if not settings.SILENT_IMPORT_CACHE: print("[CACHE] Using cached version of {0}".format(url)) - with open(cached.on_disk, 'rb') as on_disk_file: + with open(cached.on_disk, "rb") as on_disk_file: return on_disk_file.read(), cached.mime_type except (ImportCacheEntry.DoesNotExist, FileNotFoundError): @@ -279,40 +292,53 @@ def fetch(url, up_auth_file='', up_base_url='', ojs_auth_file=''): print("[CACHE] Fetching remote version of {0}".format(url)) headers = { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/39.0.2171.95 Safari/537.36'} + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 Safari/537.36" + } # disable SSL checking requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # setup auth variables do_auth = False - username = '' - password = '' + username = "" + password = "" session = requests.Session() # first, check whether there's an auth file - if up_auth_file != '': - with open(up_auth_file, 'r', encoding="utf-8") as auth_in: + if up_auth_file != "": + with open(up_auth_file, "r", encoding="utf-8") as auth_in: auth_dict = jason.loads(auth_in.read()) do_auth = True - username = auth_dict['username'] - password = auth_dict['password'] + username = auth_dict["username"] + password = auth_dict["password"] if do_auth: # load the login page - auth_url = '{0}{1}'.format(up_base_url, '/login/') - fetched = session.get(auth_url, headers=headers, stream=True, verify=False) - csrf_token = get_input_value_by_name(fetched.content, 'csrfmiddlewaretoken') - - post_dict = {'username': username, 'password': password, 'login': 'login', - 'csrfmiddlewaretoken': csrf_token} - fetched = session.post('{0}{1}'.format(up_base_url, '/login/'), data=post_dict, - headers={'Referer': auth_url, - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/39.0.2171.95 Safari/537.36' - }) + auth_url = "{0}{1}".format(up_base_url, "/login/") + fetched = session.get( + auth_url, headers=headers, stream=True, verify=False + ) + csrf_token = get_input_value_by_name( + fetched.content, "csrfmiddlewaretoken" + ) + + post_dict = { + "username": username, + "password": password, + "login": "login", + "csrfmiddlewaretoken": csrf_token, + } + fetched = session.post( + "{0}{1}".format(up_base_url, "/login/"), + data=post_dict, + headers={ + "Referer": auth_url, + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 Safari/537.36", + }, + ) if not settings.SILENT_IMPORT_CACHE: print("[CACHE] Sending auth") @@ -324,27 +350,27 @@ def fetch(url, up_auth_file='', up_base_url='', ojs_auth_file=''): resp += chunk # set the filename to a unique UUID4 identifier with the passed file extension - filename = '{0}'.format(uuid4()) + filename = "{0}".format(uuid4()) # set the path to save to be the sub-directory for the article - path = os.path.join(settings.BASE_DIR, 'files', 'import_cache') + path = os.path.join(settings.BASE_DIR, "files", "import_cache") # create the sub-folders as necessary if not os.path.exists(path): os.makedirs(path, 0o0775) - with open(os.path.join(path, filename), 'wb') as f: + with open(os.path.join(path, filename), "wb") as f: f.write(resp) ImportCacheEntry.objects.update_or_create( url=url, defaults=dict( - mime_type=fetched.headers.get('content-type'), - on_disk=os.path.join(path, filename) + mime_type=fetched.headers.get("content-type"), + on_disk=os.path.join(path, filename), ), ) - return resp, fetched.headers.get('content-type') + return resp, fetched.headers.get("content-type") def __str__(self): return self.url diff --git a/src/utils/notify.py b/src/utils/notify.py index 4e864f6ccd..4c6bda0d21 100755 --- a/src/utils/notify.py +++ b/src/utils/notify.py @@ -9,7 +9,6 @@ def notification(**kwargs): - # kwargs should include: # action: a list of frameworks to use or "all". NB Email cannot be sent by using "all". # permissions: a list of roles for channel-based notification services. @@ -25,11 +24,16 @@ def load_modules(): res = {} # check subfolders from utils import notify - plugin_dir = os.listdir(os.path.join(os.path.dirname(os.path.abspath(notify.__file__)), "notify_plugins")) + + plugin_dir = os.listdir( + os.path.join( + os.path.dirname(os.path.abspath(notify.__file__)), "notify_plugins" + ) + ) # load the modules for f in plugin_dir: - if f.startswith('.'): + if f.startswith("."): # Ignore development files continue diff --git a/src/utils/notify_helpers.py b/src/utils/notify_helpers.py index aea8bf7c3d..2f45167eac 100755 --- a/src/utils/notify_helpers.py +++ b/src/utils/notify_helpers.py @@ -9,48 +9,59 @@ def send_slack(request, slack_message, slack_channels): notify_contents = { - 'slack_message': slack_message, - 'action': slack_channels, - 'request': request, + "slack_message": slack_message, + "action": slack_channels, + "request": request, } notify.notification(**notify_contents) def send_email_with_body_from_setting_template( - request, template, subject, to, context, log_dict=None, - custom_reply_to=None, plugin=None, - ): + request, + template, + subject, + to, + context, + log_dict=None, + custom_reply_to=None, + plugin=None, +): notify_contents = { - 'subject': subject, - 'to': to, - 'html': render_template.get_message_content( + "subject": subject, + "to": to, + "html": render_template.get_message_content( request, context, template, plugin=plugin, ), - 'action': ['email'], - 'request': request, - 'log_dict': log_dict, - 'custom_reply_to': custom_reply_to, + "action": ["email"], + "request": request, + "log_dict": log_dict, + "custom_reply_to": custom_reply_to, } notify.notification(**notify_contents) def send_email_with_body_from_user( - request, subject, to, body, - log_dict=None, cc=None, - bcc=None, attachment=None, + request, + subject, + to, + body, + log_dict=None, + cc=None, + bcc=None, + attachment=None, ): notify_contents = { - 'subject': subject, - 'to': to, - 'html': body, - 'action': ['email'], - 'request': request, - 'log_dict': log_dict, - 'cc': cc, - 'bcc': bcc, - 'attachment': attachment, + "subject": subject, + "to": to, + "html": body, + "action": ["email"], + "request": request, + "log_dict": log_dict, + "cc": cc, + "bcc": bcc, + "attachment": attachment, } notify.notification(**notify_contents) diff --git a/src/utils/notify_plugins/email_log.py b/src/utils/notify_plugins/email_log.py index 0dc50d7736..a1ee07bd7b 100755 --- a/src/utils/notify_plugins/email_log.py +++ b/src/utils/notify_plugins/email_log.py @@ -7,44 +7,66 @@ def notify_hook(**kwargs): # action is a list of notification targets # if the "all" variable is passed, then some types of notification might act, like Slack. # Email, though, should only send if it's specifically an email in action, not on "all". - action = kwargs.pop('action', []) + action = kwargs.pop("action", []) - if 'email_log' not in action: + if "email_log" not in action: # email is only sent if list of actions includes "email" return # pop the args - log_dict = kwargs.pop('log_dict', None) + log_dict = kwargs.pop("log_dict", None) if log_dict: - types = log_dict.get('types') - action_text = log_dict.get('action_text') - level = log_dict.get('level') - request = kwargs.pop('request') - target = log_dict.get('target') - html = kwargs.pop('html') - to = kwargs.pop('to') - response = kwargs.pop('response') - email_subject = kwargs.pop('email_subject') - cc = kwargs.pop('cc') - bcc = kwargs.pop('bcc') - - if hasattr(request, 'user'): + types = log_dict.get("types") + action_text = log_dict.get("action_text") + level = log_dict.get("level") + request = kwargs.pop("request") + target = log_dict.get("target") + html = kwargs.pop("html") + to = kwargs.pop("to") + response = kwargs.pop("response") + email_subject = kwargs.pop("email_subject") + cc = kwargs.pop("cc") + bcc = kwargs.pop("bcc") + + if hasattr(request, "user"): actor = request.user else: actor = None if isinstance(response, dict): - message_id = response['response'].get('id') - models.LogEntry.add_entry(types=types, description=html, level=level, - request=request, target=target, is_email=True, to=to, - message_id=message_id, subject=action_text, actor=actor, - email_subject=email_subject, cc=cc, bcc=bcc) + message_id = response["response"].get("id") + models.LogEntry.add_entry( + types=types, + description=html, + level=level, + request=request, + target=target, + is_email=True, + to=to, + message_id=message_id, + subject=action_text, + actor=actor, + email_subject=email_subject, + cc=cc, + bcc=bcc, + ) else: - models.LogEntry.add_entry(types=types, description=html, level=level, is_email=True, - request=request, target=target, subject=action_text, to=to, actor=actor, - email_subject=email_subject, cc=cc, bcc=bcc) + models.LogEntry.add_entry( + types=types, + description=html, + level=level, + is_email=True, + request=request, + target=target, + subject=action_text, + to=to, + actor=actor, + email_subject=email_subject, + cc=cc, + bcc=bcc, + ) def plugin_loaded(): diff --git a/src/utils/notify_plugins/notify_email.py b/src/utils/notify_plugins/notify_email.py index 6e1f83ed87..ca32f5ee52 100755 --- a/src/utils/notify_plugins/notify_email.py +++ b/src/utils/notify_plugins/notify_email.py @@ -12,22 +12,34 @@ from utils import setting_handler from utils import notify -SANITIZE_FROM_RE = re.compile("\r|\n|\t|\"|<|>|,") +SANITIZE_FROM_RE = re.compile('\r|\n|\t|"|<|>|,') + def sanitize_from(from_): return re.sub(SANITIZE_FROM_RE, "", from_) def send_email( - subject, to, html, journal, request, - bcc=None, cc=None, attachment=None, replyto=None, + subject, + to, + html, + journal, + request, + bcc=None, + cc=None, + attachment=None, + replyto=None, ): if journal: - from_email = setting_handler.get_setting('general', 'from_address', journal).value + from_email = setting_handler.get_setting( + "general", "from_address", journal + ).value html = "{0}
{1}".format(html, journal.name) elif request.repository: # fetches the default setting for this email. - subject = setting_handler.get_email_subject_setting('email_subject', subject, journal=None) + subject = setting_handler.get_email_subject_setting( + "email_subject", subject, journal=None + ) from_email = request.press.main_contact else: from_email = request.press.main_contact @@ -40,18 +52,22 @@ def send_email( elif isinstance(to, Iterable): to = [email for email in to if not settings.DUMMY_EMAIL_DOMAIN in email] - if request and request.user and not request.user.is_anonymous and request.user.email not in to: + if ( + request + and request.user + and not request.user.is_anonymous + and request.user.email not in to + ): reply_to = [request.user.email] - full_from_string = "\"{0}\" <{1}>".format( + full_from_string = '"{0}" <{1}>'.format( sanitize_from(request.user.full_name()), from_email, ) else: reply_to = [] if request: - full_from_string = "\"{0}\" <{1}>".format( - sanitize_from(request.site_type.name), - from_email + full_from_string = '"{0}" <{1}>'.format( + sanitize_from(request.site_type.name), from_email ) else: full_from_string = from_email @@ -61,7 +77,8 @@ def send_email( full_from_string = parseaddr(force_str(full_from_string)) # As per #3545, not all backends sanitize from string before .send() full_from_string = sanitize_address( - full_from_string, settings.DEFAULT_CHARSET, + full_from_string, + settings.DEFAULT_CHARSET, ) # if a replyto is passed to this function, use that. @@ -72,7 +89,9 @@ def send_email( # replyto_address and use that. if not reply_to: custom_reply_to = setting_handler.get_setting( - 'general', 'replyto_address', journal, + "general", + "replyto_address", + journal, ).value if custom_reply_to: reply_to = (custom_reply_to,) @@ -89,7 +108,9 @@ def send_email( # Avoid empty mailboxes for servers not compliant with RFC 5322 kwargs["reply_to"] = reply_to - msg = EmailMultiAlternatives(subject, strip_tags(html), full_from_string, to, **kwargs) + msg = EmailMultiAlternatives( + subject, strip_tags(html), full_from_string, to, **kwargs + ) msg.attach_alternative(html, "text/html") if attachment: @@ -98,8 +119,8 @@ def send_email( msg.attach(file_.name, file_.read(), file_.content_type) file_.close() - elif request and request.FILES and request.FILES.getlist('attachment'): - for file in request.FILES.getlist('attachment'): + elif request and request.FILES and request.FILES.getlist("attachment"): + for file in request.FILES.getlist("attachment"): file.open() msg.attach(file.name, file.read(), file.content_type) file.close() @@ -112,28 +133,26 @@ def notify_hook(**kwargs): # action is a list of notification targets # if the "all" variable is passed, then some types of notification might act, like Slack. # Email, though, should only send if it's specifically an email in action, not on "all". - action = kwargs.pop('action', []) + action = kwargs.pop("action", []) - if 'email' not in action: + if "email" not in action: # email is only sent if list of actions includes "email" return # pop the args - subject = kwargs.pop('subject', '') - to = kwargs.pop('to', '') - html = kwargs.pop('html', '') - bcc = kwargs.pop('bcc', []) - cc = kwargs.pop('cc', []) - attachment = kwargs.pop('attachment', None) - request = kwargs.pop('request', None) - task = kwargs.pop('task', None) - custom_reply_to = kwargs.pop('custom_reply_to', None) + subject = kwargs.pop("subject", "") + to = kwargs.pop("to", "") + html = kwargs.pop("html", "") + bcc = kwargs.pop("bcc", []) + cc = kwargs.pop("cc", []) + attachment = kwargs.pop("attachment", None) + request = kwargs.pop("request", None) + task = kwargs.pop("task", None) + custom_reply_to = kwargs.pop("custom_reply_to", None) if request: subject_setting_value = setting_handler.get_email_subject_setting( - 'email_subject', - subject, - request.journal + "email_subject", subject, request.journal ) if request.journal: subject = f"[{request.journal.code}] {subject_setting_value}" @@ -143,33 +162,44 @@ def notify_hook(**kwargs): # call the method if not task: response = send_email( - subject, to, html, request.journal, - request, bcc, cc, attachment, + subject, + to, + html, + request.journal, + request, + bcc, + cc, + attachment, replyto=custom_reply_to, ) else: response = send_email( - task.email_subject, task.email_to, task.email_html, - task.email_journal, request,task.email_bcc, task.email_cc, + task.email_subject, + task.email_to, + task.email_html, + task.email_journal, + request, + task.email_bcc, + task.email_cc, replyto=custom_reply_to, ) - log_dict = kwargs.get('log_dict', None) + log_dict = kwargs.get("log_dict", None) if not type(to) in [list, tuple, set]: to = [to] if log_dict: notify_contents = { - 'log_dict': log_dict, - 'request': request, - 'response': response, - 'action': ['email_log'], - 'html': html, - 'to': to, - 'email_subject': subject, - 'cc': cc, - 'bcc': bcc, + "log_dict": log_dict, + "request": request, + "response": response, + "action": ["email_log"], + "html": html, + "to": to, + "email_subject": subject, + "cc": cc, + "bcc": bcc, } notify.notification(**notify_contents) diff --git a/src/utils/notify_plugins/notify_slack.py b/src/utils/notify_plugins/notify_slack.py index cdf6cd8ead..ee38e4fd43 100755 --- a/src/utils/notify_plugins/notify_slack.py +++ b/src/utils/notify_plugins/notify_slack.py @@ -14,45 +14,58 @@ def notify_hook(**kwargs): # if the "all" variable is passed, then some types of notification might act, like Slack. # slack_message should be an appropriate object type for the webhook # request should be the current request - action = kwargs.pop('action', []) + action = kwargs.pop("action", []) - if 'slack_editors' not in action and 'slack_admins' not in action and 'all' not in action: + if ( + "slack_editors" not in action + and "slack_admins" not in action + and "all" not in action + ): # slack responds to "all" and "slack_" commands return # pop the args - slack_message = kwargs.pop('slack_message', '') - slack_message = slack_message.replace('
', '\n').replace('
', '\n') - request = kwargs.pop('request', None) + slack_message = kwargs.pop("slack_message", "") + slack_message = slack_message.replace("
", "\n").replace("
", "\n") + request = kwargs.pop("request", None) if request and request.journal: if request.journal.slack_logging_enabled: try: - slack_webhook = setting_handler.get_setting('general', 'slack_webhook', request.journal).value + slack_webhook = setting_handler.get_setting( + "general", "slack_webhook", request.journal + ).value journal_name = request.journal.code # reformat the HTML into a slack-recognized format - message = {"text": u"[{0}] {1}".format(journal_name, slack_message), "icon_emoji": ":ghost:"} + message = { + "text": "[{0}] {1}".format(journal_name, slack_message), + "icon_emoji": ":ghost:", + } slack_json = json.dumps(message) # call the method - if 'slack_editors' in action: - notify_webhook.send_message(slack_webhook, - slack_json, headers={'content-type': 'application/json'}) + if "slack_editors" in action: + notify_webhook.send_message( + slack_webhook, + slack_json, + headers={"content-type": "application/json"}, + ) - if 'slack_admins' in action: - notify_webhook.send_message(slack_webhook, - slack_json, headers={'content-type': 'application/json'}) + if "slack_admins" in action: + notify_webhook.send_message( + slack_webhook, + slack_json, + headers={"content-type": "application/json"}, + ) except IndexError: if settings.DEBUG: - print('There is no slack webhook registered for this journal.') + print("There is no slack webhook registered for this journal.") else: pass elif settings.DEBUG: - logger.debug( - '[SLACK] {}'.format(slack_message) - ) + logger.debug("[SLACK] {}".format(slack_message)) def plugin_loaded(): diff --git a/src/utils/notify_plugins/notify_webhook.py b/src/utils/notify_plugins/notify_webhook.py index 5e86778715..36ec37a73d 100755 --- a/src/utils/notify_plugins/notify_webhook.py +++ b/src/utils/notify_plugins/notify_webhook.py @@ -4,7 +4,9 @@ def send_message(hook_url, message, headers=None): # this sends a non-blocking post to a web URL - hook_thread = threading.Thread(target=requests.post, args=(hook_url, message, headers), kwargs={}) + hook_thread = threading.Thread( + target=requests.post, args=(hook_url, message, headers), kwargs={} + ) hook_thread.start() @@ -15,15 +17,15 @@ def notify_hook(**kwargs): # html should be an appropriate object type for the webhook # headers should be a dictionary of headers - action = kwargs.pop('action', []) + action = kwargs.pop("action", []) - if 'webhook' not in action: + if "webhook" not in action: return # pop the args - html = kwargs.pop('html', '') - url = kwargs.pop('url', '') - headers = kwargs.pop('headers', '') + html = kwargs.pop("html", "") + url = kwargs.pop("url", "") + headers = kwargs.pop("headers", "") send_message(url, html, headers) diff --git a/src/utils/oidc.py b/src/utils/oidc.py index 12f7cb6e94..50c601093c 100644 --- a/src/utils/oidc.py +++ b/src/utils/oidc.py @@ -14,25 +14,25 @@ class JanewayOIDCAuthenticationCallbackView(OIDCAuthenticationCallbackView): def success_url(self): # Pull the next url from the session or settings--we don't need to # sanitize here because it should already have been sanitized. - next_url = self.request.session.get('oidc_login_next', None) + next_url = self.request.session.get("oidc_login_next", None) self.request.session.is_oidc = True - return next_url or reverse('website_index') + return next_url or reverse("website_index") class JanewayOIDCAB(OIDCAuthenticationBackend): def create_user(self, claims): user = super(JanewayOIDCAB, self).create_user(claims) - user.first_name = claims.get('given_name', '') - user.last_name = claims.get('family_name', '') + user.first_name = claims.get("given_name", "") + user.last_name = claims.get("family_name", "") user.is_active = True user.save() return user def update_user(self, user, claims): - user.first_name = claims.get('given_name', '') - user.last_name = claims.get('family_name', '') + user.first_name = claims.get("given_name", "") + user.last_name = claims.get("family_name", "") user.is_active = True user.save() @@ -40,4 +40,4 @@ def update_user(self, user, claims): def logout_url(request): - return reverse('website_index') + return reverse("website_index") diff --git a/src/utils/orcid.py b/src/utils/orcid.py index efc16b0252..1e41a8325f 100755 --- a/src/utils/orcid.py +++ b/src/utils/orcid.py @@ -19,8 +19,8 @@ logger = get_logger(__name__) -def retrieve_tokens(authorization_code, site, action='login'): - """ Retrieves the access token for the given code +def retrieve_tokens(authorization_code, site, action="login"): + """Retrieves the access token for the given code :param authorization_code: (str) code provided by ORCID :site: Object implementing the AbstractSiteModel interface @@ -36,7 +36,7 @@ def retrieve_tokens(authorization_code, site, action='login'): } content_length = len(urlencode(access_token_req)) - access_token_req['content-length'] = str(content_length) + access_token_req["content-length"] = str(content_length) base_url = settings.ORCID_TOKEN_URL logger.info("Connecting with ORCID on %s" % base_url) @@ -53,22 +53,28 @@ def retrieve_tokens(authorization_code, site, action='login'): return orcid_id -def build_redirect_uri(site, action='login'): - """ builds the landing page for ORCID requests +def build_redirect_uri(site, action="login"): + """builds the landing page for ORCID requests :site: Object implementing the AbstractSiteModel interface :return: (str) Redirect URI for ORCID requests """ request = logic.get_current_request() - return request.site_type.site_url(reverse("core_login_orcid"), - query={'state': action}) + return request.site_type.site_url( + reverse("core_login_orcid"), query={"state": action} + ) + def get_orcid_record(orcid): try: logger.info("Retrieving ORCiD profile for %s", orcid) api_client = OrcidAPI(settings.ORCID_CLIENT_ID, settings.ORCID_CLIENT_SECRET) search_token = api_client.get_search_token_from_orcid() - return api_client.read_record_public(orcid, 'record', search_token,) + return api_client.read_record_public( + orcid, + "record", + search_token, + ) except HTTPError as e: logger.info("Couldn't retrieve profile with ORCID %s", orcid) logger.info(e) @@ -78,6 +84,7 @@ def get_orcid_record(orcid): return None + def get_affiliation(summary): if len(summary["employments"]["employment-summary"]): return summary["employments"]["employment-summary"][0]["organization"] @@ -86,19 +93,17 @@ def get_affiliation(summary): else: return None + def get_orcid_record_details(orcid): details = defaultdict(lambda: None) record = get_orcid_record(orcid) if record: - details["uri"] = record['orcid-identifier']['uri'] - details["orcid"] = record['orcid-identifier']['path'] + details["uri"] = record["orcid-identifier"]["uri"] + details["orcid"] = record["orcid-identifier"]["path"] user_record = record["person"] # Order matters here, we want to get emails first in case anything # goes wrong with person details below - details["emails"] = [ - email["email"] - for email in user_record["emails"]["email"] - ] + details["emails"] = [email["email"] for email in user_record["emails"]["email"]] name = user_record.get("name", None) if name: diff --git a/src/utils/plugins.py b/src/utils/plugins.py index b29abe78c7..45394db69d 100644 --- a/src/utils/plugins.py +++ b/src/utils/plugins.py @@ -21,7 +21,7 @@ class Plugin: article_pk_in_handshake_url = False press_wide = False - kanban_card = '{plugin_name}/kanban_card.html'.format( + kanban_card = "{plugin_name}/kanban_card.html".format( plugin_name=plugin_name, ) @@ -30,7 +30,7 @@ def install(cls): plugin, created = cls.get_or_create_plugin_object() if not created and plugin.version != cls.version: - print('Plugin updated: {0} -> {1}'.format(cls.version, plugin.version)) + print("Plugin updated: {0} -> {1}".format(cls.version, plugin.version)) plugin.version = cls.version plugin.save() @@ -45,10 +45,10 @@ def get_or_create_plugin_object(cls): plugin, created = models.Plugin.objects.get_or_create( name=cls.short_name, defaults={ - 'display_name': cls.display_name, - 'version': cls.version, - 'enabled': True, - 'press_wide': cls.press_wide, + "display_name": cls.display_name, + "version": cls.version, + "enabled": True, + "press_wide": cls.press_wide, }, ) @@ -61,11 +61,13 @@ def get_self(cls): name=cls.short_name, ) except models.Plugin.MultipleObjectsReturned: - plugin = models.Plugin.objects.filter( - name=cls.short_name, - ).order_by( - '-version' - ).first() + plugin = ( + models.Plugin.objects.filter( + name=cls.short_name, + ) + .order_by("-version") + .first() + ) except models.Plugin.DoesNotExist: return None diff --git a/src/utils/render_template.py b/src/utils/render_template.py index 1d9ccb0742..ca0b9879ee 100755 --- a/src/utils/render_template.py +++ b/src/utils/render_template.py @@ -8,16 +8,14 @@ from utils import setting_handler -def get_message_content(request, context, template, plugin=False, template_is_setting=False): +def get_message_content( + request, context, template, plugin=False, template_is_setting=False +): if plugin: - template = setting_handler.get_plugin_setting( - plugin, - template, - None - ).value + template = setting_handler.get_plugin_setting(plugin, template, None).value elif not template_is_setting: template = setting_handler.get_setting( - 'email', + "email", template, request.journal, ).value @@ -30,8 +28,7 @@ def get_message_content(request, context, template, plugin=False, template_is_se return html_content -def get_requestless_content(context, journal, template, group_name='email'): - +def get_requestless_content(context, journal, template, group_name="email"): template = setting_handler.get_setting(group_name, template, journal).value template = Template(template) diff --git a/src/utils/sessions/janeway_db.py b/src/utils/sessions/janeway_db.py index bb0b3d80ed..01e56309fb 100644 --- a/src/utils/sessions/janeway_db.py +++ b/src/utils/sessions/janeway_db.py @@ -6,9 +6,7 @@ class SessionStore(db.SessionStore): - @classmethod @transaction.atomic def clear_expired(cls): - cls.get_model_class().objects.filter( - expire_date__lt=timezone.now()).delete() + cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete() diff --git a/src/utils/setting_handler.py b/src/utils/setting_handler.py index d3bbe75341..76b3ad3d3b 100755 --- a/src/utils/setting_handler.py +++ b/src/utils/setting_handler.py @@ -20,13 +20,13 @@ def create_setting( - setting_group_name, - setting_name, - type, - pretty_name, - description, - is_translatable=True, - default_value=None, + setting_group_name, + setting_name, + type, + pretty_name, + description, + is_translatable=True, + default_value=None, ): # If the setting is translatable use current lang, else use the default. lang = translation.get_language() if is_translatable else settings.LANGUAGE_CODE @@ -39,11 +39,11 @@ def create_setting( group=group, name=setting_name, defaults={ - 'types': type, - 'pretty_name': pretty_name, - 'description': description, - 'is_translatable': is_translatable, - } + "types": type, + "pretty_name": pretty_name, + "description": description, + "is_translatable": is_translatable, + }, ) if created and default_value: @@ -64,19 +64,19 @@ def get_or_create_default_setting(setting, default_value): setting=setting, journal=None, defaults={ - 'value': default_value, - } + "value": default_value, + }, ) return setting def get_setting( - setting_group_name, - setting_name, - journal, - create=False, - default=True, + setting_group_name, + setting_name, + journal, + create=False, + default=True, ): """ Returns a matching SettingValue for the current language. @@ -111,7 +111,11 @@ def get_setting( except ObjectDoesNotExist as e: e.args += (setting_name, setting_group_name) raise e - lang = translation.get_language() if setting.is_translatable else settings.LANGUAGE_CODE + lang = ( + translation.get_language() + if setting.is_translatable + else settings.LANGUAGE_CODE + ) with translation.override(lang): try: @@ -160,26 +164,27 @@ def save_setting(setting_group_name, setting_name, journal, value): name=setting_name, group__name=setting_group_name, ) - lang = translation.get_language() if setting.is_translatable else settings.LANGUAGE_CODE + lang = ( + translation.get_language() + if setting.is_translatable + else settings.LANGUAGE_CODE + ) with translation.override(lang): - setting_value, created = core_models.SettingValue.objects \ - .get_or_create( - setting__group=setting.group, - setting=setting, - journal=journal + setting_value, created = core_models.SettingValue.objects.get_or_create( + setting__group=setting.group, setting=setting, journal=journal ) if created: # Ensure that a value exists for settings.LANGUAGE_CODE - setattr(setting, 'value_{0}'.format(settings.LANGUAGE_CODE), '') + setattr(setting, "value_{0}".format(settings.LANGUAGE_CODE), "") setting_value.save() - if setting.types == 'json' and isinstance(value, (list, dict)): + if setting.types == "json" and isinstance(value, (list, dict)): value = json.dumps(value) - if setting.types == 'boolean': - value = 'on' if value else '' + if setting.types == "boolean": + value = "on" if value else "" setting_value.value = value setting_value.save() @@ -188,7 +193,7 @@ def save_setting(setting_group_name, setting_name, journal, value): def save_plugin_setting(plugin, setting_name, value, journal): - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=plugin.name) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=plugin.name) setting = save_setting( setting_group_name=plugin_group_name, setting_name=setting_name, @@ -199,14 +204,9 @@ def save_plugin_setting(plugin, setting_name, value, journal): def get_plugin_setting( - plugin, - setting_name, - journal, - create=False, - pretty='', - types='Text,' + plugin, setting_name, journal, create=False, pretty="", types="Text," ): - plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=plugin.name) + plugin_group_name = "plugin:{plugin_name}".format(plugin_name=plugin.name) try: return get_setting( setting_group_name=plugin_group_name, @@ -222,7 +222,7 @@ def get_plugin_setting( setting_name=setting_name, type=types, pretty_name=pretty, - description='', + description="", is_translatable=False, ) @@ -235,14 +235,16 @@ def get_plugin_setting( def get_email_subject_setting( - setting_group="email_subject", - setting_name=None, - journal=None, - default=True, + setting_group="email_subject", + setting_name=None, + journal=None, + default=True, ): try: setting = core_models.Setting.objects.get(name=setting_name) - return get_setting(setting_group, setting.name, journal, create=False, default=True).value + return get_setting( + setting_group, setting.name, journal, create=False, default=True + ).value except (core_models.Setting.DoesNotExist, AttributeError): return setting_name @@ -264,29 +266,37 @@ def toggle_boolean_setting(setting_name, setting_group_name, journal): def fetch_defaults_value(setting): - with codecs.open(os.path.join(settings.BASE_DIR, 'utils/install/journal_defaults.json'), 'r+', encoding='utf-8') as json_data: + with codecs.open( + os.path.join(settings.BASE_DIR, "utils/install/journal_defaults.json"), + "r+", + encoding="utf-8", + ) as json_data: default_data = json.load(json_data) for item in default_data: - if item['setting']['name'] == setting.get('name'): - return item['value']['default'] + if item["setting"]["name"] == setting.get("name"): + return item["value"]["default"] def update_settings(settings_to_change, journal): - print('Updating {journal} settings... '.format(journal=journal.code)) + print("Updating {journal} settings... ".format(journal=journal.code)) for setting in settings_to_change: - try: - setting_object = get_setting(setting.get('group'), setting.get('name'), journal) + setting_object = get_setting( + setting.get("group"), setting.get("name"), journal + ) - if setting.get('action', None) == 'update': - print('Updating {setting}, action: {action}'.format(setting=setting.get('name'), - action=setting.get('action'))) - check = input('If you want to update this setting, respond with y: ') - if check == 'y': + if setting.get("action", None) == "update": + print( + "Updating {setting}, action: {action}".format( + setting=setting.get("name"), action=setting.get("action") + ) + ) + check = input("If you want to update this setting, respond with y: ") + if check == "y": defaults_value = fetch_defaults_value(setting) setting_object.value = defaults_value setting_object.save() - elif setting.get('action', None) == 'drop': + elif setting.get("action", None) == "drop": setting_object.delete() except BaseException as e: print(e) diff --git a/src/utils/shared.py b/src/utils/shared.py index 11f493da5c..6e4a00e289 100755 --- a/src/utils/shared.py +++ b/src/utils/shared.py @@ -32,11 +32,11 @@ def get_ip_address(request): if request is None: return None - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: - ip = x_forwarded_for.split(',')[0] + ip = x_forwarded_for.split(",")[0] else: - ip = request.META.get('REMOTE_ADDR') # Real IP address of client Machine + ip = request.META.get("REMOTE_ADDR") # Real IP address of client Machine return ip @@ -50,16 +50,19 @@ def guess_extension(mime): :param mime: a mimetype string :return: a string containing a file extension eg. doc or docx """ - if mime == 'text/plain': - extension = 'txt' - elif mime == 'application/msword': - extension = 'doc' - elif mime == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': - extension = 'docx' - elif mime == 'application/vnd.oasis.opendocument.text': - extension = 'odt' - elif mime == 'text/html;charset=UTF-8': - extension = 'html' + if mime == "text/plain": + extension = "txt" + elif mime == "application/msword": + extension = "doc" + elif ( + mime + == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ): + extension = "docx" + elif mime == "application/vnd.oasis.opendocument.text": + extension = "odt" + elif mime == "text/html;charset=UTF-8": + extension = "html" else: extension = mimetypes.guess_extension(mime) @@ -68,10 +71,10 @@ def guess_extension(mime): def yes_or_no(question): while "the answer is invalid": - reply = str(input(question + ' (y/n): ')).lower().strip() - if reply[0] == 'y': + reply = str(input(question + " (y/n): ")).lower().strip() + if reply[0] == "y": return True - if reply[0] == 'n': + if reply[0] == "n": return False @@ -91,7 +94,7 @@ def set_order(objects, order_attr_name, pk_list): return objects - + def day_month(date): return date.strftime("%d-%b") @@ -104,21 +107,21 @@ def make_timezone_aware(date_string, date_string_format): def create_language_override_redirect( - request, - url_name, - kwargs, - query_strings=None, + request, + url_name, + kwargs, + query_strings=None, ): if not query_strings: query_strings = {} - query_strings['language'] = request.override_language + query_strings["language"] = request.override_language if "email_template" in request.GET: - query_strings['email_template'] = 'true' + query_strings["email_template"] = "true" reverse_string = "{reverse}?{params}".format( reverse=reverse(url_name, kwargs=kwargs), - params=urlencode(query_strings, quote_via=quote_plus) + params=urlencode(query_strings, quote_via=quote_plus), ) return reverse_string diff --git a/src/utils/template_override_middleware.py b/src/utils/template_override_middleware.py index f058fed67b..86c402288d 100755 --- a/src/utils/template_override_middleware.py +++ b/src/utils/template_override_middleware.py @@ -15,19 +15,21 @@ class Loader(FileSystemLoader): - @staticmethod @function_cache.cache(120) def journal_theme(journal): - return setting_handler.get_setting('general', 'journal_theme', journal).value + return setting_handler.get_setting("general", "journal_theme", journal).value @staticmethod @function_cache.cache(120) def base_theme(journal): - return setting_handler.get_setting('general', 'journal_base_theme', journal).value + return setting_handler.get_setting( + "general", "journal_base_theme", journal + ).value def get_theme_dirs(self): from core import models as core_models + request = utils_logic.get_current_request() base_theme, theme_setting = None, None @@ -45,10 +47,10 @@ def get_theme_dirs(self): pass elif request.repository: - theme_setting = 'OLH' + theme_setting = "OLH" if ( - request.repository.theme and - request.repository.theme in settings.REPOSITORY_THEMES + request.repository.theme + and request.repository.theme in settings.REPOSITORY_THEMES ): theme_setting = request.repository.theme else: @@ -56,26 +58,31 @@ def get_theme_dirs(self): theme_setting = request.press.theme # allows servers in debug mode to override the theme with ?theme=name in the URL - if settings.DEBUG and request and request.GET.get('theme'): - theme_setting = request.GET.get('theme') + if settings.DEBUG and request and request.GET.get("theme"): + theme_setting = request.GET.get("theme") # order up the themes and return them with engine dirs themes_in_order = list() if theme_setting: themes_in_order.append( - os.path.join(settings.BASE_DIR, 'themes', theme_setting, 'templates') + os.path.join(settings.BASE_DIR, "themes", theme_setting, "templates") ) if base_theme: themes_in_order.append( - os.path.join(settings.BASE_DIR, 'themes', base_theme, 'templates') + os.path.join(settings.BASE_DIR, "themes", base_theme, "templates") ) # if the base_theme and INSTALLATION_BASE_THEME are different, # append the INSTALLATION_BASE_THEME. if not base_theme == settings.INSTALLATION_BASE_THEME: themes_in_order.append( - os.path.join(settings.BASE_DIR, 'themes', settings.INSTALLATION_BASE_THEME, 'templates') + os.path.join( + settings.BASE_DIR, + "themes", + settings.INSTALLATION_BASE_THEME, + "templates", + ) ) return themes_in_order + self.engine.dirs diff --git a/src/utils/testing/helpers.py b/src/utils/testing/helpers.py index 0b39f7bceb..b816cd7597 100755 --- a/src/utils/testing/helpers.py +++ b/src/utils/testing/helpers.py @@ -44,7 +44,7 @@ def create_user(username, roles=None, journal=None, **attrs): if roles is None: roles = [] - kwargs = {'username': username} + kwargs = {"username": username} try: user = core_models.Account.objects.get(email=username) except core_models.Account.DoesNotExist: @@ -57,9 +57,7 @@ def create_user(username, roles=None, journal=None, **attrs): create_roles(roles) resolved_role = core_models.Role.objects.get(slug=role) core_models.AccountRole.objects.get_or_create( - user=user, - role=resolved_role, - journal=journal + user=user, role=resolved_role, journal=journal ) for attr, value in attrs.items(): @@ -81,8 +79,8 @@ def create_roles(roles=None): for role in roles: core_models.Role.objects.get_or_create( - name=role.replace('-', ' ').capitalize(), - slug=role.lower().replace(' ', '-') + name=role.replace("-", " ").capitalize(), + slug=role.lower().replace(" ", "-"), ) @@ -99,8 +97,8 @@ def create_journals(): journal_two = journal_models.Journal(code="TSA", domain="journal2.localhost") journal_two.save() - journal_one.name = 'Journal One' - journal_two.name = 'Journal Two' + journal_one.name = "Journal One" + journal_two.name = "Journal Two" update_issue_types(journal_one) update_issue_types(journal_two) @@ -108,7 +106,9 @@ def create_journals(): def create_press(): - return press_models.Press.objects.create(name='Press', domain='localhost', main_contact='a@b.com') + return press_models.Press.objects.create( + name="Press", domain="localhost", main_contact="a@b.com" + ) def create_issue(journal, vol=0, number=0, articles=None): @@ -116,15 +116,15 @@ def create_issue(journal, vol=0, number=0, articles=None): code="issue", journal=journal, ) - issue_datetime = get_aware_datetime('2022-01-01') + issue_datetime = get_aware_datetime("2022-01-01") issue, created = journal_models.Issue.objects.get_or_create( journal=journal, issue=number, volume=vol, defaults={ - 'issue_title': ('Test Issue from Utils Testing Helpers'), - 'issue_type': issue_type, - 'date': issue_datetime, + "issue_title": ("Test Issue from Utils Testing Helpers"), + "issue_type": issue_type, + "date": issue_datetime, }, ) if articles: @@ -155,8 +155,8 @@ def create_editor(journal, **kwargs): def create_section_editor(journal, **kwargs): - email = kwargs.pop('email', 'section_editor@example.com') - roles = ['section-editor'] + email = kwargs.pop("email", "section_editor@example.com") + roles = ["section-editor"] se = create_user(email, roles=roles, journal=journal) se.is_active = True se.save() @@ -164,8 +164,8 @@ def create_section_editor(journal, **kwargs): def create_peer_reviewer(journal, **kwargs): - email = kwargs.pop('email', 'peer_reviewer@example.com') - roles = ['reviewer'] + email = kwargs.pop("email", "peer_reviewer@example.com") + roles = ["reviewer"] reviewer = create_user(email, roles=roles, journal=journal) reviewer.is_active = True reviewer.save() @@ -173,15 +173,15 @@ def create_peer_reviewer(journal, **kwargs): def create_author(journal, **kwargs): - roles = kwargs.pop('roles', ['author']) - email = kwargs.pop('email', "authoruser@martineve.com") + roles = kwargs.pop("roles", ["author"]) + email = kwargs.pop("email", "authoruser@martineve.com") attrs = { "first_name": "Author", "middle_name": "A", "last_name": "User", "institution": "Author institution", "department": "Author Department", - "biography": "Author test biography" + "biography": "Author test biography", } attrs.update(kwargs) author = create_user( @@ -196,20 +196,19 @@ def create_author(journal, **kwargs): def create_article(journal, **kwargs): - article = sm_models.Article.objects.create( journal=journal, - title='Test Article from Utils Testing Helpers', - article_agreement='Test Article', + title="Test Article from Utils Testing Helpers", + article_agreement="Test Article", section=create_section(journal), ) - if kwargs.pop('with_author', False): - author_kwargs ={ - 'salutation': 'Dr.', - 'name_suffix': 'Jr.', - 'orcid': '1234-5678-9012-345X', - 'email': '{}{}'.format(uuid4(), settings.DUMMY_EMAIL_DOMAIN) + if kwargs.pop("with_author", False): + author_kwargs = { + "salutation": "Dr.", + "name_suffix": "Jr.", + "orcid": "1234-5678-9012-345X", + "email": "{}{}".format(uuid4(), settings.DUMMY_EMAIL_DOMAIN), } author = create_author(journal, **author_kwargs) article.authors.add(author) @@ -218,8 +217,8 @@ def create_article(journal, **kwargs): author.snapshot_self(article) else: article.save() - for k,v in kwargs.items(): - setattr(article, k ,v) + for k, v in kwargs.items(): + setattr(article, k, v) article.save() return article @@ -230,7 +229,7 @@ def create_galley(article, file_obj=None, **kwargs): article_id=article.pk, label="file", is_galley=True, - uuid_filename="test.txt" + uuid_filename="test.txt", ) galley = core_models.Galley.objects.create( article_id=article.pk, @@ -243,9 +242,9 @@ def create_galley(article, file_obj=None, **kwargs): def create_section(journal, **kwargs): defaults = { - 'number_of_reviewers': 2, - 'name': 'Article', - 'plural': 'Articles', + "number_of_reviewers": 2, + "name": "Article", + "plural": "Articles", } defaults.update(kwargs) @@ -258,16 +257,16 @@ def create_section(journal, **kwargs): def create_submission( owner=None, - title='A Test Article', - abstract='A Test article abstract', + title="A Test Article", + abstract="A Test article abstract", journal_id=1, stage=sm_models.STAGE_UNASSIGNED, authors=None, **kwargs, ): - section, _ = sm_models.Section.objects.get_or_create( - journal__id=journal_id, name="Article", + journal__id=journal_id, + name="Article", ) article = sm_models.Article.objects.create( owner=owner, @@ -276,7 +275,7 @@ def create_submission( journal_id=journal_id, stage=stage, section=section, - **kwargs + **kwargs, ) if authors: article.authors.add(*authors) @@ -285,8 +284,8 @@ def create_submission( def create_test_file(test_case, file): - label = 'Test File' - path_parts = ('articles', test_case.article_in_production.pk) + label = "Test File" + path_parts = ("articles", test_case.article_in_production.pk) file = files.save_file( test_case.request, @@ -301,14 +300,14 @@ def create_test_file(test_case, file): return file, path_parts -def create_repository(press, managers, subject_editors, domain='repo.domain.com'): +def create_repository(press, managers, subject_editors, domain="repo.domain.com"): repository, c = repo_models.Repository.objects.get_or_create( press=press, - name='Test Repository', - short_name='testrepo', - object_name='Preprint', - object_name_plural='Preprints', - publisher='Test Publisher', + name="Test Repository", + short_name="testrepo", + object_name="Preprint", + object_name_plural="Preprints", + publisher="Test Publisher", live=True, domain=domain, ) @@ -317,8 +316,8 @@ def create_repository(press, managers, subject_editors, domain='repo.domain.com' subject, c = repo_models.Subject.objects.get_or_create( repository=repository, - name='Repo Subject', - slug='repo-subject', + name="Repo Subject", + slug="repo-subject", enabled=True, ) subject.editors.add( @@ -328,14 +327,14 @@ def create_repository(press, managers, subject_editors, domain='repo.domain.com' return repository, subject -def create_preprint(repository, author, subject, title='This is a Test Preprint'): +def create_preprint(repository, author, subject, title="This is a Test Preprint"): preprint = repo_models.Preprint.objects.create( repository=repository, owner=author, stage=repo_models.STAGE_PREPRINT_REVIEW, title=title, - abstract='This is a fake abstract.', - comments_editor='', + abstract="This is a fake abstract.", + comments_editor="", date_submitted=timezone.now(), ) preprint.subject.add( @@ -343,8 +342,8 @@ def create_preprint(repository, author, subject, title='This is a Test Preprint' ) file = repo_models.PreprintFile.objects.create( preprint=preprint, - original_filename='fake_file.pdf', - mime_type='application/pdf', + original_filename="fake_file.pdf", + mime_type="application/pdf", size=100, ) preprint.submission_file = file @@ -352,7 +351,7 @@ def create_preprint(repository, author, subject, title='This is a Test Preprint' preprint=preprint, account=author, order=1, - affiliation='Made Up University', + affiliation="Made Up University", ) return preprint @@ -370,7 +369,7 @@ def __init__(self): self.secure = False self.user = False self.FILES = None - self.META = {'REMOTE_ADDR': '127.0.0.1'} + self.META = {"REMOTE_ADDR": "127.0.0.1"} self.model_content_type = None def is_secure(self): @@ -380,7 +379,7 @@ def is_secure(self): return True def get_host(self): - return 'testserver' + return "testserver" class activate_translation(ContextDecorator): @@ -396,7 +395,6 @@ def __exit__(self, *exc): class request_context(ContextDecorator): - def __init__(self, request, *args, **kwargs): super().__init__(*args, **kwargs) self.request = request @@ -410,10 +408,7 @@ def __exit__(self, *exc): def create_review_form(journal): return review_models.ReviewForm.objects.create( - name="A Form", - intro="i", - thanks="t", - journal=journal + name="A Form", intro="i", thanks="t", journal=journal ) @@ -425,24 +420,24 @@ def create_round(article, round_number=1): def create_review_assignment( - journal=None, - article=None, - reviewer=None, - editor=None, - due_date=None, - review_form=None, - decision=None, - is_complete=False, - review_round=None, - review_file=None, - ): + journal=None, + article=None, + reviewer=None, + editor=None, + due_date=None, + review_form=None, + decision=None, + is_complete=False, + review_round=None, + review_file=None, +): if not journal: journal, _journal_two = create_journals() if not article: article = create_submission( owner=create_regular_user(), journal_id=journal.pk, - stage=sm_models.STAGE_UNDER_REVIEW + stage=sm_models.STAGE_UNDER_REVIEW, ) if not reviewer: reviewer = create_second_user(journal) @@ -474,40 +469,37 @@ def create_review_assignment( def create_reminder(journal=None, reminder_type=None): from cron.models import Reminder + if not journal: journal, _journal_two = create_journals() if not reminder_type: - reminder_type='review' + reminder_type = "review" reminder = Reminder.objects.create( journal=journal, - type='review', - run_type='before', + type="review", + run_type="before", days=3, - template_name='test_reminder_'+reminder_type, - subject='Test reminder subject', + template_name="test_reminder_" + reminder_type, + subject="Test reminder subject", ) from utils import setting_handler + setting_handler.create_setting( - 'email', + "email", reminder.template_name, - 'rich-text', + "rich-text", reminder.subject, - '', - is_translatable=True - ) - setting_handler.save_setting( - 'email', - reminder.template_name, - journal, - 'Test body' + "", + is_translatable=True, ) + setting_handler.save_setting("email", reminder.template_name, journal, "Test body") return reminder def create_editor_assignment(article, editor, **kwargs): - assignment_type = kwargs.get('assignment_type','editor') + assignment_type = kwargs.get("assignment_type", "editor") assignment, created = review_models.EditorAssignment.objects.get_or_create( article=article, editor=editor, @@ -517,11 +509,11 @@ def create_editor_assignment(article, editor, **kwargs): def create_revision_request(article, editor, **kwargs): - note = kwargs.get('note', 'Test note') - decision = kwargs.get('decision', review_models.review_decision()[0][0]) + note = kwargs.get("note", "Test note") + decision = kwargs.get("decision", review_models.review_decision()[0][0]) if isinstance(decision, tuple): decision = decision[0] - date_due = kwargs.get('date_due', timezone.now() + datetime.timedelta(days=3)) + date_due = kwargs.get("date_due", timezone.now() + datetime.timedelta(days=3)) revision = review_models.RevisionRequest.objects.create( article=article, editor=editor, @@ -533,19 +525,19 @@ def create_revision_request(article, editor, **kwargs): def create_copyeditor(journal, **kwargs): - username = kwargs.pop('username', 'copyeditor@example.com') - roles = kwargs.pop('roles', ['copyeditor']) + username = kwargs.pop("username", "copyeditor@example.com") + roles = kwargs.pop("roles", ["copyeditor"]) return create_user(username, roles=roles, journal=journal, **kwargs) def create_copyedit_assignment(article, copyeditor, **kwargs): - editor = kwargs.get('editor', None) - assigned = kwargs.get('assigned', timezone.now() - datetime.timedelta(minutes=3)) - notified = kwargs.get('notified', False) - decision = kwargs.get('decision', False) - date_decided = kwargs.get('date_decided', None) - copyeditor_completed = kwargs.get('copyeditor_completed', None) - copyedit_accepted = kwargs.get('copyedit_accepted', None) + editor = kwargs.get("editor", None) + assigned = kwargs.get("assigned", timezone.now() - datetime.timedelta(minutes=3)) + notified = kwargs.get("notified", False) + decision = kwargs.get("decision", False) + date_decided = kwargs.get("date_decided", None) + copyeditor_completed = kwargs.get("copyeditor_completed", None) + copyedit_accepted = kwargs.get("copyedit_accepted", None) assignment = copyediting_models.CopyeditAssignment.objects.create( article=article, @@ -567,22 +559,22 @@ def create_access_request(journal, user, role, **kwargs): journal=journal, user=user, role=role, - text='Automatic request as author added to an article.', + text="Automatic request as author added to an article.", ) return access_request def create_news_item(content_type, object_id, **kwargs): - title = kwargs.get('title', 'Test title') - body = kwargs.get('body', 'Test body') + title = kwargs.get("title", "Test title") + body = kwargs.get("body", "Test body") posted_by = kwargs.get( - 'posted_by', + "posted_by", create_user( - 'news_author@example.org', - attrs={'first_name': 'News', 'last_name': 'Writer'} - ) + "news_author@example.org", + attrs={"first_name": "News", "last_name": "Writer"}, + ), ) - tags = kwargs.get('tags', ['test tag 1', 'test tag 2']) + tags = kwargs.get("tags", ["test tag 1", "test tag 2"]) item = comms_models.NewsItem.objects.create( content_type=content_type, object_id=object_id, @@ -597,10 +589,10 @@ def create_news_item(content_type, object_id, **kwargs): def create_cms_page(content_type, object_id, **kwargs): - name = kwargs.get('name', 'test-name') - display_name = kwargs.get('display_name', 'Test display name') - content = kwargs.get('content', 'Test content') - is_markdown = kwargs.get('is_markdown', False) + name = kwargs.get("name", "test-name") + display_name = kwargs.get("display_name", "Test display name") + content = kwargs.get("content", "Test content") + is_markdown = kwargs.get("is_markdown", False) return cms_models.Page.objects.create( content_type=content_type, @@ -613,9 +605,9 @@ def create_cms_page(content_type, object_id, **kwargs): def create_contact(content_type, object_id, **kwargs): - name = kwargs.get('name', 'Test Contact') - email = kwargs.get('email', 'contact@example.org') - role = kwargs.get('role', 'Test contact role') + name = kwargs.get("name", "Test Contact") + email = kwargs.get("email", "contact@example.org") + role = kwargs.get("role", "Test contact role") return core_models.Contacts.objects.create( content_type=content_type, object_id=object_id, @@ -624,15 +616,16 @@ def create_contact(content_type, object_id, **kwargs): role=role, ) + def create_setting( - setting_group_name='test_group', - setting_name='test_setting', - setting_type='rich-text', - pretty_name='Test Setting', - description='A test setting.', - is_translatable=True, - default_value='Default setting value', - ): + setting_group_name="test_group", + setting_name="test_setting", + setting_type="rich-text", + pretty_name="Test Setting", + description="A test setting.", + is_translatable=True, + default_value="Default setting value", +): return setting_handler.create_setting( setting_group_name, setting_name, diff --git a/src/utils/tests.py b/src/utils/tests.py index 5ce3bc1616..8c6703565f 100644 --- a/src/utils/tests.py +++ b/src/utils/tests.py @@ -61,28 +61,33 @@ def setUpModule(): class UtilsTests(TestCase): - @classmethod def setUpTestData(cls): - call_command('load_default_settings') + call_command("load_default_settings") cls.press = helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() - helpers.create_roles(['reviewer', 'editor', 'author', 'section-editor']) + helpers.create_roles(["reviewer", "editor", "author", "section-editor"]) - cls.journal_one = journal_models.Journal.objects.get(code="TST", domain="testserver") + cls.journal_one = journal_models.Journal.objects.get( + code="TST", domain="testserver" + ) cls.regular_user = helpers.create_regular_user() cls.second_user = helpers.create_second_user(cls.journal_one) cls.editor = helpers.create_editor(cls.journal_one) - cls.editor_two = helpers.create_editor(cls.journal_one, email='editor2@example.com') + cls.editor_two = helpers.create_editor( + cls.journal_one, email="editor2@example.com" + ) cls.section_editor = helpers.create_section_editor(cls.journal_one) cls.author = helpers.create_author(cls.journal_one) - cls.coauthor = helpers.create_author(cls.journal_one, email='coauthor@example.org') + cls.coauthor = helpers.create_author( + cls.journal_one, email="coauthor@example.org" + ) cls.copyeditor = helpers.create_copyeditor(cls.journal_one) setting_handler.save_setting( - 'general', - 'submission_access_request_contact', + "general", + "submission_access_request_contact", cls.journal_one, cls.editor.email, ) @@ -92,9 +97,9 @@ def setUpTestData(cls): cls.submitted_article.correspondence_author = cls.author cls.submitted_article.authors.add(cls.coauthor) - cls.review_form = review_models.ReviewForm.objects.create(name="A Form", intro="i", thanks="t", - journal=cls.journal_one) - + cls.review_form = review_models.ReviewForm.objects.create( + name="A Form", intro="i", thanks="t", journal=cls.journal_one + ) cls.article_under_review = helpers.create_article( journal=cls.journal_one, @@ -107,16 +112,18 @@ def setUpTestData(cls): cls.article_under_review.authors.add(cls.author) cls.article_under_review.authors.add(cls.coauthor) - cls.review_assignment = review_models.ReviewAssignment.objects.create(article=cls.article_under_review, - reviewer=cls.second_user, - editor=cls.editor, - date_due=timezone.now(), - form=cls.review_form) + cls.review_assignment = review_models.ReviewAssignment.objects.create( + article=cls.article_under_review, + reviewer=cls.second_user, + editor=cls.editor, + date_due=timezone.now(), + form=cls.review_form, + ) cls.access_request = helpers.create_access_request( cls.journal_one, cls.author, - 'author', + "author", ) cls.request = helpers.Request() @@ -124,54 +131,57 @@ def setUpTestData(cls): cls.request.press = cls.journal_one.press cls.request.site_type = cls.journal_one cls.request.user = cls.editor - cls.request.model_content_type = ContentType.objects.get_for_model(cls.request.journal) + cls.request.model_content_type = ContentType.objects.get_for_model( + cls.request.journal + ) - cls.test_message = 'This message is a test for outgoing email, nothing else.' + cls.test_message = "This message is a test for outgoing email, nothing else." cls.base_kwargs = { - 'request': cls.request, - 'user_message_content': cls.test_message, - 'skip': False, + "request": cls.request, + "user_message_content": cls.test_message, + "skip": False, } # Setup issues for sitemap testing cls.issue_type, created = journal_models.IssueType.objects.get_or_create( journal=cls.journal_one, - code='test_issue_type', - pretty_name='Test Issue Type', - custom_plural='Test Issues Type', + code="test_issue_type", + pretty_name="Test Issue Type", + custom_plural="Test Issues Type", ) cls.issue_one, created = journal_models.Issue.objects.get_or_create( journal=cls.journal_one, - volume='1', - issue='1', - issue_title='V 1 I 1', - issue_type=cls.issue_type + volume="1", + issue="1", + issue_title="V 1 I 1", + issue_type=cls.issue_type, ) cls.section, create = submission_models.Section.objects.get_or_create( journal=cls.journal_one, - name='Test Section', + name="Test Section", ) cls.article_one, created = submission_models.Article.objects.get_or_create( journal=cls.journal_one, owner=cls.author, - title='This is a test article', - abstract='This is an abstract', + title="This is a test article", + abstract="This is an abstract", stage=submission_models.STAGE_PUBLISHED, section=cls.section, defaults={ - 'date_accepted': timezone.now(), - 'date_published': timezone.now(), - } + "date_accepted": timezone.now(), + "date_published": timezone.now(), + }, ) cls.issue_one.articles.add(cls.article_one) - # Helper function for email subjects def get_default_email_subject(self, setting_name, journal=None): journal = journal or self.journal_one - group = 'email_subject' - subject = setting_handler.get_email_subject_setting(group, setting_name, journal) + group = "email_subject" + subject = setting_handler.get_email_subject_setting( + group, setting_name, journal + ) if subject != setting_name: # The test shouldn't pass unless the setting or a default was retrieved. # The name serves as a backup in production but shouldn't be let through in testing. @@ -179,10 +189,8 @@ def get_default_email_subject(self, setting_name, journal=None): class SitemapTests(UtilsTests): - @override_settings(URL_CONFIG="path") def test_press_sitemap_generation(self): - expected_press_sitemap = """ @@ -266,14 +274,14 @@ class TransactionalReviewEmailTests(UtilsTests): def test_send_reviewer_withdrawl_notice(self): kwargs = { - 'review_assignment': self.review_assignment, - 'request': self.request, - 'user_message_content': self.test_message, - 'skip': False + "review_assignment": self.review_assignment, + "request": self.request, + "user_message_content": self.test_message, + "skip": False, } expected_recipient = self.review_assignment.reviewer.email - subject_setting_name = 'subject_review_withdrawl' + subject_setting_name = "subject_review_withdrawl" subject_setting = self.get_default_email_subject(subject_setting_name) email_data = core_email.EmailData( subject=subject_setting, @@ -289,14 +297,13 @@ def test_send_reviewer_withdrawl_notice(self): expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) - def test_send_editor_unassigned_notice(self): expected_recipient_one = self.review_assignment.editor.email - subject_setting_name = 'subject_unassign_editor' + subject_setting_name = "subject_unassign_editor" subject_setting = self.get_default_email_subject( - subject_setting_name, - journal=self.review_assignment.article.journal, - ) + subject_setting_name, + journal=self.review_assignment.article.journal, + ) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) email_data = core_email.EmailData( @@ -318,7 +325,7 @@ def test_send_editor_assigned_acknowledgements(self): article=self.article_under_review, editor=self.editor_two, ) - subject_setting_name = 'subject_editor_assignment' + subject_setting_name = "subject_editor_assignment" subject_setting = self.get_default_email_subject(subject_setting_name) email_data = core_email.EmailData( subject=subject_setting, @@ -327,9 +334,9 @@ def test_send_editor_assigned_acknowledgements(self): expected_recipient_one = editor_assignment.editor.email kwargs = dict(**self.base_kwargs) - kwargs['editor_assignment'] = editor_assignment - kwargs['acknowledgment'] = True - kwargs['email_data'] = email_data + kwargs["editor_assignment"] = editor_assignment + kwargs["acknowledgment"] = True + kwargs["email_data"] = email_data send_editor_assigned_acknowledgements(**kwargs) self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) @@ -339,22 +346,21 @@ def test_send_editor_assigned_acknowledgements(self): def test_send_reviewer_requested_acknowledgements(self): kwargs = dict(**self.base_kwargs) - kwargs['review_assignment'] = self.review_assignment + kwargs["review_assignment"] = self.review_assignment expected_recipient_one = self.review_assignment.reviewer.email send_reviewer_requested_acknowledgements(**kwargs) self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_review_assignment' + subject_setting_name = "subject_review_assignment" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) - def test_send_review_complete_acknowledgements(self): kwargs = dict(**self.base_kwargs) - kwargs['review_assignment'] = self.review_assignment + kwargs["review_assignment"] = self.review_assignment send_review_complete_acknowledgements(**kwargs) @@ -362,7 +368,7 @@ def test_send_review_complete_acknowledgements(self): expected_recipient_one = self.review_assignment.reviewer.email self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_review_complete_reviewer_acknowledgement' + subject_setting_name = "subject_review_complete_reviewer_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) @@ -371,25 +377,24 @@ def test_send_review_complete_acknowledgements(self): expected_recipient_two = self.review_assignment.editor.email self.assertEqual(expected_recipient_two, mail.outbox[1].to[0]) - subject_setting_name = 'subject_review_complete_acknowledgement' + subject_setting_name = "subject_review_complete_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[1].subject) - def test_send_reviewer_accepted_or_decline_acknowledgements(self): kwargs = dict(**self.base_kwargs) - kwargs['review_assignment'] = self.review_assignment + kwargs["review_assignment"] = self.review_assignment # reviewer accepted - kwargs['accepted'] = True + kwargs["accepted"] = True send_reviewer_accepted_or_decline_acknowledgements(**kwargs) # first email expected_recipient_one = self.review_assignment.reviewer.email self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_review_accept_acknowledgement' + subject_setting_name = "subject_review_accept_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) @@ -398,21 +403,20 @@ def test_send_reviewer_accepted_or_decline_acknowledgements(self): expected_recipient_two = self.review_assignment.editor.email self.assertEqual(expected_recipient_two, mail.outbox[1].to[0]) - subject_setting_name = 'subject_reviewer_acknowledgement' + subject_setting_name = "subject_reviewer_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[1].subject) - # reviewer declined - kwargs['accepted'] = False + kwargs["accepted"] = False send_reviewer_accepted_or_decline_acknowledgements(**kwargs) # first email expected_recipient_one = self.review_assignment.reviewer.email self.assertEqual(expected_recipient_one, mail.outbox[2].to[0]) - subject_setting_name = 'subject_review_decline_acknowledgement' + subject_setting_name = "subject_review_decline_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[2].subject) @@ -421,12 +425,11 @@ def test_send_reviewer_accepted_or_decline_acknowledgements(self): expected_recipient_two = self.review_assignment.editor.email self.assertEqual(expected_recipient_two, mail.outbox[3].to[0]) - subject_setting_name = 'subject_reviewer_acknowledgement' + subject_setting_name = "subject_reviewer_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[3].subject) - def test_send_submission_acknowledgement(self): """ Tests whether subjects are correct, nothing else. @@ -435,30 +438,30 @@ def test_send_submission_acknowledgement(self): """ kwargs = dict(**self.base_kwargs) - kwargs['article'] = self.submitted_article + kwargs["article"] = self.submitted_article send_submission_acknowledgement(**kwargs) # first email subject - subject_setting_name = 'subject_submission_acknowledgement' + subject_setting_name = "subject_submission_acknowledgement" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) # second email subject - subject_setting_name = 'subject_editor_new_submission' + subject_setting_name = "subject_editor_new_submission" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[1].subject) def test_send_article_decision(self): kwargs = dict(**self.base_kwargs) - kwargs['article'] = self.article_under_review + kwargs["article"] = self.article_under_review expected_recipient_one = self.article_under_review.correspondence_author.email - for i, decision in enumerate(['accept', 'decline']): # to be added: 'undecline' - kwargs['decision'] = decision - subject_setting_name = f'subject_review_decision_{decision}' + for i, decision in enumerate(["accept", "decline"]): # to be added: 'undecline' + kwargs["decision"] = decision + subject_setting_name = f"subject_review_decision_{decision}" subject_setting = self.get_default_email_subject(subject_setting_name) email_data = core_email.EmailData( subject=subject_setting, @@ -470,15 +473,16 @@ def test_send_article_decision(self): self.assertEqual(expected_recipient_one, mail.outbox[i].to[0]) - expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) + expected_subject = "[{0}] {1}".format( + self.journal_one.code, subject_setting + ) self.assertEqual(expected_subject, mail.outbox[i].subject) - def test_send_revisions_request(self): kwargs = dict(**self.base_kwargs) - kwargs['revision'] = helpers.create_revision_request( - article = self.article_under_review, - editor = self.editor, + kwargs["revision"] = helpers.create_revision_request( + article=self.article_under_review, + editor=self.editor, ) send_revisions_request(**kwargs) @@ -486,17 +490,16 @@ def test_send_revisions_request(self): expected_recipient_one = self.article_under_review.correspondence_author.email self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_request_revisions' + subject_setting_name = "subject_request_revisions" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) - def test_send_revisions_complete(self): kwargs = dict(**self.base_kwargs) - kwargs['revision'] = helpers.create_revision_request( - article = self.article_under_review, - editor = self.editor, + kwargs["revision"] = helpers.create_revision_request( + article=self.article_under_review, + editor=self.editor, ) send_revisions_complete(**kwargs) @@ -504,17 +507,16 @@ def test_send_revisions_complete(self): expected_recipient_one = self.editor.email self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_revisions_complete_receipt' + subject_setting_name = "subject_revisions_complete_receipt" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) - def test_send_revisions_author_receipt(self): kwargs = dict(**self.base_kwargs) - kwargs['revision'] = helpers.create_revision_request( - article = self.article_under_review, - editor = self.editor, + kwargs["revision"] = helpers.create_revision_request( + article=self.article_under_review, + editor=self.editor, ) send_revisions_author_receipt(**kwargs) @@ -522,35 +524,35 @@ def test_send_revisions_author_receipt(self): expected_recipient_one = self.article_under_review.correspondence_author.email self.assertEqual(expected_recipient_one, mail.outbox[0].to[0]) - subject_setting_name = 'subject_revisions_complete_receipt' + subject_setting_name = "subject_revisions_complete_receipt" subject_setting = self.get_default_email_subject(subject_setting_name) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) class CopyeditingEmailSubjectTests(UtilsTests): - """ This class covers email subjects used in transactional_emails - with the exception of review emails (covered above) and + with the exception of review emails (covered above) and production and typesetting (not being actively developed) """ + def test_copyediting_email_subjects(self): for email_function, subject_setting_name in ( - (send_copyedit_assignment, 'subject_copyeditor_assignment_notification'), - (send_copyedit_updated, 'subject_copyedit_updated'), - (send_copyedit_deleted, 'subject_copyedit_deleted'), - (send_copyedit_decision, 'subject_copyediting_decision'), - (send_copyedit_author_review, 'subject_copyeditor_notify_author'), - (send_copyedit_complete, 'subject_copyeditor_notify_editor' ), - (send_copyedit_ack, 'subject_copyeditor_ack' ), - (send_copyedit_reopen, 'subject_copyeditor_reopen_task' ), - (send_author_copyedit_complete, 'subject_author_copyedit_complete'), - ): + (send_copyedit_assignment, "subject_copyeditor_assignment_notification"), + (send_copyedit_updated, "subject_copyedit_updated"), + (send_copyedit_deleted, "subject_copyedit_deleted"), + (send_copyedit_decision, "subject_copyediting_decision"), + (send_copyedit_author_review, "subject_copyeditor_notify_author"), + (send_copyedit_complete, "subject_copyeditor_notify_editor"), + (send_copyedit_ack, "subject_copyeditor_ack"), + (send_copyedit_reopen, "subject_copyeditor_reopen_task"), + (send_author_copyedit_complete, "subject_author_copyedit_complete"), + ): subject_setting = self.get_default_email_subject( - subject_setting_name, - journal=self.article_under_review.journal, - ) + subject_setting_name, + journal=self.article_under_review.journal, + ) email_data = core_email.EmailData( subject=subject_setting, @@ -558,38 +560,41 @@ def test_copyediting_email_subjects(self): ) kwargs = dict(**self.base_kwargs) kwargs["email_data"] = email_data - expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) - kwargs['copyedit_assignment'] = helpers.create_copyedit_assignment( - article = self.article_under_review, - copyeditor = self.copyeditor, - editor = self.editor, + expected_subject = "[{0}] {1}".format( + self.journal_one.code, subject_setting + ) + kwargs["copyedit_assignment"] = helpers.create_copyedit_assignment( + article=self.article_under_review, + copyeditor=self.copyeditor, + editor=self.editor, ) - kwargs['copyedit'] = kwargs['copyedit_assignment'] - kwargs['decision'] = 'test_decision' - kwargs['article'] = self.article_under_review - kwargs['author_review'], created = copyediting_models.AuthorReview.objects.get_or_create( - author=self.author, - assignment=kwargs['copyedit'], - notified=True + kwargs["copyedit"] = kwargs["copyedit_assignment"] + kwargs["decision"] = "test_decision" + kwargs["article"] = self.article_under_review + kwargs["author_review"], created = ( + copyediting_models.AuthorReview.objects.get_or_create( + author=self.author, assignment=kwargs["copyedit"], notified=True + ) ) email_data = core_email.EmailData( subject=subject_setting, body=self.test_message, ) email_function(**kwargs) - expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) + expected_subject = "[{0}] {1}".format( + self.journal_one.code, subject_setting + ) self.assertEqual(expected_subject, mail.outbox[-1].subject) class PrepubEmailTests(UtilsTests): - def test_send_prepub_notifications(self): - request = self.base_kwargs['request'] + request = self.base_kwargs["request"] article = self.article_under_review helpers.create_editor_assignment( article, self.section_editor, - assignment_type='section-editor', + assignment_type="section-editor", ) reviewer = helpers.create_peer_reviewer(self.journal_one) review_assignment = helpers.create_review_assignment( @@ -600,13 +605,13 @@ def test_send_prepub_notifications(self): ) review_assignment.is_complete = True review_assignment.date_declined = None - review_assignment.decision = 'yes' + review_assignment.decision = "yes" review_assignment.save() form_kwargs = { - 'email_context': { - 'article': article, + "email_context": { + "article": article, }, - 'request': request, + "request": request, } initial = journal_logic.get_initial_for_prepub_notifications( request, @@ -615,19 +620,15 @@ def test_send_prepub_notifications(self): form_data = { "form-TOTAL_FORMS": len(initial), "form-INITIAL_FORMS": "0", - "form-0-to": initial[0]['to'], - "form-0-cc": initial[0]['cc'], + "form-0-to": initial[0]["to"], + "form-0-cc": initial[0]["cc"], "form-0-subject": "Article Publication", - "form-0-body": self.journal_one.get_setting( - 'email', - 'author_publication' - ), - "form-1-to": initial[1]['to'], - "form-1-bcc": initial[1]['bcc'], + "form-0-body": self.journal_one.get_setting("email", "author_publication"), + "form-1-to": initial[1]["to"], + "form-1-bcc": initial[1]["bcc"], "form-1-subject": "Article You Reviewed", "form-1-body": self.journal_one.get_setting( - 'email', - 'peer_reviewer_pub_notification' + "email", "peer_reviewer_pub_notification" ), } formset = journal_forms.PrepubNotificationFormSet( @@ -640,14 +641,16 @@ def test_send_prepub_notifications(self): article=article, formset=formset, ) - subject_setting = self.get_default_email_subject('subject_author_publication') + subject_setting = self.get_default_email_subject("subject_author_publication") expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertIn(self.author.email, mail.outbox[0].to) self.assertIn(self.coauthor.email, mail.outbox[0].cc) self.assertIn(self.section_editor.email, mail.outbox[0].cc) self.assertEqual(expected_subject, mail.outbox[0].subject) - subject_setting = self.get_default_email_subject('subject_peer_reviewer_pub_notification') + subject_setting = self.get_default_email_subject( + "subject_peer_reviewer_pub_notification" + ) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertNotIn(self.author.email, mail.outbox[1].to) self.assertNotIn(self.author.email, mail.outbox[1].cc) @@ -665,95 +668,102 @@ class MiscEmailSubjectTests(UtilsTests): """ def test_send_draft_decison(self): - kwargs = dict(**self.base_kwargs) - kwargs['article'] = self.article_under_review - kwargs['draft'], created = review_models.DecisionDraft.objects.get_or_create( - article=kwargs['article'], + kwargs["article"] = self.article_under_review + kwargs["draft"], created = review_models.DecisionDraft.objects.get_or_create( + article=kwargs["article"], editor=self.editor, section_editor=self.section_editor, - message_to_editor='Test Message', + message_to_editor="Test Message", ) send_draft_decison(**kwargs) - subject_setting = self.get_default_email_subject('subject_draft_editor_message') + subject_setting = self.get_default_email_subject("subject_draft_editor_message") expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) def test_send_draft_decision_declined(self): kwargs = dict(**self.base_kwargs) - kwargs['article'] = self.article_under_review - kwargs['draft_decision'], created = review_models.DecisionDraft.objects.get_or_create( - article=kwargs['article'], - editor=self.editor, - section_editor=self.section_editor, - message_to_editor='Test Message', + kwargs["article"] = self.article_under_review + kwargs["draft_decision"], created = ( + review_models.DecisionDraft.objects.get_or_create( + article=kwargs["article"], + editor=self.editor, + section_editor=self.section_editor, + message_to_editor="Test Message", + ) ) send_draft_decision_declined(**kwargs) - subject_setting = self.get_default_email_subject('subject_notify_se_draft_declined') + subject_setting = self.get_default_email_subject( + "subject_notify_se_draft_declined" + ) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) def test_access_request_notification(self): kwargs = dict(**self.base_kwargs) - kwargs['access_request'] = self.access_request + kwargs["access_request"] = self.access_request access_request_notification(**kwargs) - subject_setting = self.get_default_email_subject('subject_submission_access_request_notification') + subject_setting = self.get_default_email_subject( + "subject_submission_access_request_notification" + ) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) def test_access_request_complete(self): kwargs = dict(**self.base_kwargs) - kwargs['access_request'] = self.access_request - kwargs['decision'] = 'Test Decision' + kwargs["access_request"] = self.access_request + kwargs["decision"] = "Test Decision" access_request_complete(**kwargs) - subject_setting = self.get_default_email_subject('subject_submission_access_request_complete') + subject_setting = self.get_default_email_subject( + "subject_submission_access_request_complete" + ) expected_subject = "[{0}] {1}".format(self.journal_one.code, subject_setting) self.assertEqual(expected_subject, mail.outbox[0].subject) class TestMergeSettings(TestCase): - def test_recursive_merge(self): base = { - "setting": "value", - "setting_a": "value_a", - "setting_list": ["value_a"], - "setting_dict": {"a": "a", "b": "a"}, + "setting": "value", + "setting_a": "value_a", + "setting_list": ["value_a"], + "setting_dict": {"a": "a", "b": "a"}, } overrides = { - "setting_a": "value_b", - "setting_list": ["value_b"], - "setting_dict": {"b": "b", "c": "c"}, - "other_setting": "value", + "setting_a": "value_b", + "setting_list": ["value_b"], + "setting_dict": {"b": "b", "c": "c"}, + "other_setting": "value", } expected = { - "setting": "value", - "setting_a": "value_b", - "setting_list": ["value_a", "value_b"], - "setting_dict": {"a": "a", "b": "b", "c": "c"}, - "other_setting": "value", + "setting": "value", + "setting_a": "value_b", + "setting_list": ["value_a", "value_b"], + "setting_dict": {"a": "a", "b": "b", "c": "c"}, + "other_setting": "value", } result = merge_settings(base, overrides) self.assertDictEqual(expected, result) -class TestForms(TestCase): +class TestForms(TestCase): @classmethod def setUpTestData(cls): helpers.create_press() helpers.create_journals() - cls.journal = journal_models.Journal.objects.get(code="TST", domain="testserver") + cls.journal = journal_models.Journal.objects.get( + code="TST", domain="testserver" + ) def test_fake_model_form(self): - class FakeTestForm(FakeModelForm): class Meta: model = journal_models.Journal @@ -765,12 +775,12 @@ class Meta: form.save() def test_keyword_form(self): - class KeywordTestForm(KeywordModelForm): class Meta: model = journal_models.Journal fields = ("code",) exclude = tuple() + expected = "Expected Keyword" data = { "keywords": "Keyword, another one, and another one,%s" % expected, @@ -783,11 +793,10 @@ class Meta: self.assertTrue(journal.keywords.filter(word=expected).exists()) def test_keyword_form_empty_string(self): - class KeywordTestForm(KeywordModelForm): class Meta: model = journal_models.Journal - fields = ('keywords', ) + fields = ("keywords",) exclude = tuple() data = {"keywords": ""} @@ -798,7 +807,6 @@ class Meta: class TestPlainTextValidator(TestCase): - def test_plain_text_validator_valid(self): name_test = "Kathryn Janeway" ampersand_test = "Voyager & co" @@ -815,7 +823,6 @@ def test_plain_text_validator_invalid(self): class TestModels(TestCase): - @classmethod def setUpTestData(cls): helpers.create_press() @@ -823,105 +830,105 @@ def setUpTestData(cls): cls.ten_articles = {helpers.create_article(cls.journal_one) for i in range(10)} def test_log_entry_bulk_add_simple_entry(self): - types = 'Submission' - pks = ','.join([str(article.pk) for article in self.ten_articles]) + types = "Submission" + pks = ",".join([str(article.pk) for article in self.ten_articles]) description = f"Sending request for article {pks}" - level = 'Info' + level = "Info" models.LogEntry.bulk_add_simple_entry( types, description, level, self.ten_articles, ) - log_entries = models.LogEntry.objects.filter( - types='Submission' - ).order_by('-date')[:10] + log_entries = models.LogEntry.objects.filter(types="Submission").order_by( + "-date" + )[:10] articles = {entry.target for entry in log_entries} self.assertSetEqual(self.ten_articles, articles) class NotifyEmail(TestCase): - @classmethod def setUpTestData(cls): helpers.create_press() cls.journal_one, cls.journal_two = helpers.create_journals() cls.request = mock.Mock() - cls.request.site_type.name = 'Mock request site type name' + cls.request.site_type.name = "Mock request site type name" cls.request.FILES = None def test_send_email_reply_to(self): args = [ - 'subject', - 'to@example.com', - 'html_body', + "subject", + "to@example.com", + "html_body", self.journal_one, self.request, ] kwargs = { - 'bcc': 'bcc@example.com', - 'cc': 'cc@example.com', - 'replyto': 'replyto@example.com', + "bcc": "bcc@example.com", + "cc": "cc@example.com", + "replyto": "replyto@example.com", } - with mock.patch('utils.notify_plugins.notify_email.EmailMultiAlternatives') as msg: + with mock.patch( + "utils.notify_plugins.notify_email.EmailMultiAlternatives" + ) as msg: notify_email.send_email(*args, **kwargs) - self.assertIn(kwargs['replyto'], msg.call_args.kwargs['reply_to']) + self.assertIn(kwargs["replyto"], msg.call_args.kwargs["reply_to"]) class TestOIDC(TestCase): - @override_settings( - OIDC_OP_TOKEN_ENDPOINT='test.janeway.systems/token', - OIDC_OP_USER_ENDPOINT='test.janeway.systems/user', - OIDC_OP_JWKS_ENDPOINT='test.janeway.systems/jwks', - OIDC_RP_CLIENT_ID='test', - OIDC_RP_CLIENT_SECRET='test', - OIDC_RP_SIGN_ALGO='RS256', + OIDC_OP_TOKEN_ENDPOINT="test.janeway.systems/token", + OIDC_OP_USER_ENDPOINT="test.janeway.systems/user", + OIDC_OP_JWKS_ENDPOINT="test.janeway.systems/jwks", + OIDC_RP_CLIENT_ID="test", + OIDC_RP_CLIENT_SECRET="test", + OIDC_RP_SIGN_ALGO="RS256", ) def test_create_user(self): oidc_auth_backend = oidc.JanewayOIDCAB() claims = { - 'given_name': 'Andy', - 'family_name': 'Byers', - 'email': 'andy@janeway.systems', + "given_name": "Andy", + "family_name": "Byers", + "email": "andy@janeway.systems", } user = oidc_auth_backend.create_user(claims=claims) self.assertEqual( user.email, - 'andy@janeway.systems', + "andy@janeway.systems", ) self.assertEqual( user.username, - 'andy@janeway.systems', + "andy@janeway.systems", ) @override_settings( - OIDC_OP_TOKEN_ENDPOINT='test.janeway.systems/token', - OIDC_OP_USER_ENDPOINT='test.janeway.systems/user', - OIDC_OP_JWKS_ENDPOINT='test.janeway.systems/jwks', - OIDC_RP_CLIENT_ID='test', - OIDC_RP_CLIENT_SECRET='test', - OIDC_RP_SIGN_ALGO='RS256', + OIDC_OP_TOKEN_ENDPOINT="test.janeway.systems/token", + OIDC_OP_USER_ENDPOINT="test.janeway.systems/user", + OIDC_OP_JWKS_ENDPOINT="test.janeway.systems/jwks", + OIDC_RP_CLIENT_ID="test", + OIDC_RP_CLIENT_SECRET="test", + OIDC_RP_SIGN_ALGO="RS256", ) def test_update_user(self): user = core_models.Account.objects.create( - first_name='Andy', - last_name='Byers', - email='andy@janeway.systems', + first_name="Andy", + last_name="Byers", + email="andy@janeway.systems", ) oidc_auth_backend = oidc.JanewayOIDCAB() claims = { - 'given_name': 'Andrew', - 'family_name': 'Byers', - 'email': 'andy@janeway.systems', + "given_name": "Andrew", + "family_name": "Byers", + "email": "andy@janeway.systems", } oidc_user = oidc_auth_backend.update_user(user, claims=claims) self.assertEqual( oidc_user.first_name, - 'Andrew', + "Andrew", ) @@ -935,15 +942,22 @@ def test_unalatered_themes(self): dirs = loader.get_theme_dirs() self.assertEqual( dirs, - [os.path.join(settings.BASE_DIR, 'themes', settings.INSTALLATION_BASE_THEME, 'templates')] + [ + os.path.join( + settings.BASE_DIR, + "themes", + settings.INSTALLATION_BASE_THEME, + "templates", + ) + ], ) def test_journal_dirs_with_theme(self): setting_handler.save_setting( - 'general', - 'journal_theme', + "general", + "journal_theme", self.journal_one, - 'LCARS', + "LCARS", ) # the middleware heavily caches these settings so we need to clear it. clear_cache() @@ -960,23 +974,23 @@ def test_journal_dirs_with_theme(self): self.assertEqual( dirs, [ - os.path.join(settings.BASE_DIR, 'themes', 'LCARS', 'templates'), - os.path.join(settings.BASE_DIR, 'themes', 'OLH', 'templates') - ] + os.path.join(settings.BASE_DIR, "themes", "LCARS", "templates"), + os.path.join(settings.BASE_DIR, "themes", "OLH", "templates"), + ], ) def test_journal_dirs_with_theme_and_base_theme(self): setting_handler.save_setting( - 'general', - 'journal_theme', + "general", + "journal_theme", self.journal_one, - 'LCARS', + "LCARS", ) setting_handler.save_setting( - 'general', - 'journal_base_theme', + "general", + "journal_base_theme", self.journal_one, - 'material', + "material", ) # the middleware heavily caches these settings so we need to clear it. clear_cache() @@ -993,14 +1007,14 @@ def test_journal_dirs_with_theme_and_base_theme(self): self.assertEqual( dirs, [ - os.path.join(settings.BASE_DIR, 'themes', 'LCARS', 'templates'), - os.path.join(settings.BASE_DIR, 'themes', 'material', 'templates'), - os.path.join(settings.BASE_DIR, 'themes', 'OLH', 'templates') - ] + os.path.join(settings.BASE_DIR, "themes", "LCARS", "templates"), + os.path.join(settings.BASE_DIR, "themes", "material", "templates"), + os.path.join(settings.BASE_DIR, "themes", "OLH", "templates"), + ], ) -class PreprintsUtilsTests(TestCase): +class PreprintsUtilsTests(TestCase): @classmethod def setUpTestData(cls): cls.press = helpers.create_press() @@ -1008,24 +1022,27 @@ def setUpTestData(cls): cls.repo_manager2 = helpers.create_user("repo_man2@example.com") cls.other_user = helpers.create_user("other@example.com") - cls.repository, cls.subject = helpers.create_repository(cls.press, [cls.repo_manager1, cls.repo_manager2], []) + cls.repository, cls.subject = helpers.create_repository( + cls.press, [cls.repo_manager1, cls.repo_manager2], [] + ) - cls.submitted_preprint = helpers.create_preprint(cls.repository, cls.other_user, cls.subject) + cls.submitted_preprint = helpers.create_preprint( + cls.repository, cls.other_user, cls.subject + ) cls.request = helpers.Request() cls.request.user = cls.other_user cls.request.repository = cls.repository cls.request.press = cls.press cls.request.site_type = cls.repository - cls.request.model_content_type = ContentType.objects.get_for_model(cls.request.repository) + cls.request.model_content_type = ContentType.objects.get_for_model( + cls.request.repository + ) -class PreprintsTransactionalEmailTests(PreprintsUtilsTests): +class PreprintsTransactionalEmailTests(PreprintsUtilsTests): def test_send_submission_notification_all_managers(self): - kwargs = { - 'request': self.request, - 'preprint': self.submitted_preprint - } + kwargs = {"request": self.request, "preprint": self.submitted_preprint} preprint_submission(**kwargs) @@ -1040,10 +1057,7 @@ def test_send_submission_notification_one(self): self.repository.submission_notification_recipients.add(self.repo_manager1) self.repository.save() - kwargs = { - 'request': self.request, - 'preprint': self.submitted_preprint - } + kwargs = {"request": self.request, "preprint": self.submitted_preprint} preprint_submission(**kwargs) @@ -1055,15 +1069,17 @@ def test_send_submission_notification_one(self): class AdminTestMeta(type): - """ A Metaclass for generating test cases directly from the admin registry + """A Metaclass for generating test cases directly from the admin registry A new test is generated for each admin class registered via the admin.py modules in the application ChildrenMustImplement: build_test_case """ + def __new__(cls, name, bases, attrs): def test_bar(instance): instance.assertTrue(False) + for test_name, test_case in cls.build_tests(): attrs[test_name] = test_case return type(name, bases, attrs) @@ -1083,8 +1099,8 @@ class AdminSearchTestMeta(AdminTestMeta): def build_test_case(model, admin_class): app_label = model._meta.app_label model_name = model._meta.model_name - url_name = f'admin:{app_label}_{model_name}_changelist' - url = reverse(url_name) + '?q=test' + url_name = f"admin:{app_label}_{model_name}_changelist" + url = reverse(url_name) + "?q=test" def test_case(instance): response = instance.client.get( @@ -1092,20 +1108,20 @@ def test_case(instance): SERVER_NAME=instance.press.domain, ) instance.assertEqual(response.status_code, 200) - test_name = f'test_admin_view_{app_label}_{model_name}' + + test_name = f"test_admin_view_{app_label}_{model_name}" return test_name, test_case class TestAdmin(TestCase, metaclass=AdminSearchTestMeta): - @classmethod def setUpClass(cls): super().setUpClass() user_model = get_user_model() cls.superuser = user_model.objects.create_superuser( - username='super', - password='secret', - email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) cls.press = helpers.create_press() @@ -1120,22 +1136,21 @@ def setUp(self): class TestBounceEmailRoutes(UtilsTests): - def test_send_bounce_notification_actor_is_editor(self): """ Tests that when an email sent by an editor bounces the bounce notification is sent to that editor. """ fake_log_entry = util_models.LogEntry.add_entry( - 'Test Log Entry', - 'This is a fake log entry', - level='Info', + "Test Log Entry", + "This is a fake log entry", + level="Info", actor=self.editor, target=self.submitted_article, is_email=True, - to='tuvix@security.voyager.fed', - message_id='12324343432', - subject='This is all just a test', + to="tuvix@security.voyager.fed", + message_id="12324343432", + subject="This is all just a test", ) logic.send_bounce_notification_to_event_actor(fake_log_entry) self.assertEqual(self.editor.email, mail.outbox[0].to[0]) @@ -1146,45 +1161,44 @@ def test_send_bounce_notification_actor_is_author(self): directed to the press' primary contact. """ fake_log_entry = util_models.LogEntry.add_entry( - 'Test Log Entry', - 'This is a fake log entry', - level='Info', + "Test Log Entry", + "This is a fake log entry", + level="Info", actor=self.author, target=self.submitted_article, is_email=True, - to='tuvix@security.voyager.fed', - message_id='12324343432', - subject='This is all just a test', + to="tuvix@security.voyager.fed", + message_id="12324343432", + subject="This is all just a test", ) logic.send_bounce_notification_to_event_actor(fake_log_entry) self.assertEqual(self.press.main_contact, mail.outbox[0].to[0]) def test_mailgun_webhook(self): - message_id = '12324343432' + message_id = "12324343432" fake_log_entry = util_models.LogEntry.add_entry( - 'Test Log Entry', - 'This is a fake log entry', - level='Info', + "Test Log Entry", + "This is a fake log entry", + level="Info", actor=self.editor, target=self.submitted_article, is_email=True, - to='tuvix@security.voyager.fed', + to="tuvix@security.voyager.fed", message_id=message_id, - subject='This is all just a test', + subject="This is all just a test", ) fake_mailgun_post = { - 'Message-Id': message_id, - 'token': '122112', - 'timestamp': timezone.now(), - 'signature': 'dsfsfsdfsdfsdfds', - 'event': 'dropped', + "Message-Id": message_id, + "token": "122112", + "timestamp": timezone.now(), + "signature": "dsfsfsdfsdfsdfds", + "event": "dropped", } logic.parse_mailgun_webhook(fake_mailgun_post) self.assertEqual(self.editor.email, mail.outbox[0].to[0]) class TestMigrationUtils(TestCase): - @classmethod def setUpTestData(cls): cls.press = helpers.create_press() @@ -1192,13 +1206,13 @@ def setUpTestData(cls): cls.setting_value = cls.setting.settingvalue_set.first() def test_update_default_setting_values(self): - with translation.override('en'): - new_value = 'Updated default setting value' + with translation.override("en"): + new_value = "Updated default setting value" migration_utils.update_default_setting_values( apps, self.setting.name, self.setting.group.name, - values_to_replace=['Default setting value'], + values_to_replace=["Default setting value"], replacement_value=new_value, ) saved_value = setting_handler.get_setting( @@ -1210,12 +1224,262 @@ def test_update_default_setting_values(self): new_value, saved_value, ) -class TestORCiDRecord(TestCase): - all_fields = {'orcid-identifier': {'uri': 'http://sandbox.orcid.org/0000-0000-0000-0000', 'path': '0000-0000-0000-0000', 'host': 'sandbox.orcid.org'}, 'preferences': {'locale': 'EN'}, 'history': {'creation-method': 'DIRECT', 'completion-date': None, 'submission-date': {'value': 1716899022299}, 'last-modified-date': {'value': 1717012729950}, 'claimed': True, 'source': None, 'deactivation-date': None, 'verified-email': True, 'verified-primary-email': True}, 'person': {'last-modified-date': {'value': 1717012710380}, 'name': {'created-date': {'value': 1716899022606}, 'last-modified-date': {'value': 1716931428927}, 'given-names': {'value': 'cdleschol'}, 'family-name': {'value': 'arship'}, 'credit-name': None, 'source': None, 'visibility': 'PUBLIC', 'path': '0000-0000-0000-0000'}, 'other-names': {'last-modified-date': None, 'other-name': [], 'path': '/0000-0000-0000-0000/other-names'}, 'biography': None, 'researcher-urls': {'last-modified-date': None, 'researcher-url': [], 'path': '/0000-0000-0000-0000/researcher-urls'}, 'emails': {'last-modified-date': {'value': 1717012710380}, 'email': [{'created-date': {'value': 1716899022599}, 'last-modified-date': {'value': 1717012710380}, 'source': {'source-orcid': {'uri': 'http://sandbox.orcid.org/0000-0000-0000-0000', 'path': '0000-0000-0000-0000', 'host': 'sandbox.orcid.org'}, 'source-client-id': None, 'source-name': {'value': 'cdleschol arship'}}, 'email': 'cdleschol@mailinator.com', 'path': None, 'visibility': 'PUBLIC', 'verified': True, 'primary': True, 'put-code': None}], 'path': '/0000-0000-0000-0000/email'}, 'addresses': {'last-modified-date': {'value': 1716931402191}, 'address': [{'created-date': {'value': 1716931402191}, 'last-modified-date': {'value': 1716931402191}, 'source': {'source-orcid': {'uri': 'http://sandbox.orcid.org/0000-0000-0000-0000', 'path': '0000-0000-0000-0000', 'host': 'sandbox.orcid.org'}, 'source-client-id': None, 'source-name': {'value': 'cdleschol arship'}}, 'country': {'value': 'US'}, 'visibility': 'PUBLIC', 'path': '/0000-0000-0000-0000/address/7884', 'put-code': 7884, 'display-index': 1}], 'path': '/0000-0000-0000-0000/address'}, 'keywords': {'last-modified-date': None, 'keyword': [], 'path': '/0000-0000-0000-0000/keywords'}, 'external-identifiers': {'last-modified-date': None, 'external-identifier': [], 'path': '/0000-0000-0000-0000/external-identifiers'}, 'path': '/0000-0000-0000-0000/person'}, 'activities-summary': {'last-modified-date': {'value': 1716931455651}, 'educations': {'last-modified-date': None, 'education-summary': [], 'path': '/0000-0000-0000-0000/educations'}, 'employments': {'last-modified-date': {'value': 1716931455651}, 'employment-summary': [{'created-date': {'value': 1716931455651}, 'last-modified-date': {'value': 1716931455651}, 'source': {'source-orcid': {'uri': 'http://sandbox.orcid.org/0000-0000-0000-0000', 'path': '0000-0000-0000-0000', 'host': 'sandbox.orcid.org'}, 'source-client-id': None, 'source-name': {'value': 'cdleschol arship'}}, 'department-name': None, 'role-title': None, 'start-date': None, 'end-date': None, 'organization': {'name': 'California Digital Library', 'address': {'city': 'Oakland', 'region': 'California', 'country': 'US'}, 'disambiguated-organization': {'disambiguated-organization-identifier': 'https://ror.org/03yrm5c26', 'disambiguation-source': 'ROR'}}, 'visibility': 'PUBLIC', 'put-code': 66225, 'path': '/0000-0000-0000-0000/employment/66225'}], 'path': '/0000-0000-0000-0000/employments'}, 'fundings': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/fundings'}, 'peer-reviews': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/peer-reviews'}, 'works': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/works'}, 'path': '/0000-0000-0000-0000/activities'}, 'path': '/0000-0000-0000-0000'} - min_fields = {'orcid-identifier': {'uri': 'http://sandbox.orcid.org/0000-0000-0000-0000', 'path': '0000-0000-0000-0000', 'host': 'sandbox.orcid.org'}, 'preferences': {'locale': 'EN'}, 'history': {'creation-method': 'DIRECT', 'completion-date': None, 'submission-date': {'value': 1716899022299}, 'last-modified-date': {'value': 1717012843372}, 'claimed': True, 'source': None, 'deactivation-date': None, 'verified-email': True, 'verified-primary-email': True}, 'person': {'last-modified-date': None, 'name': None, 'other-names': {'last-modified-date': None, 'other-name': [], 'path': '/0000-0000-0000-0000/other-names'}, 'biography': None, 'researcher-urls': {'last-modified-date': None, 'researcher-url': [], 'path': '/0000-0000-0000-0000/researcher-urls'}, 'emails': {'last-modified-date': None, 'email': [], 'path': '/0000-0000-0000-0000/email'}, 'addresses': {'last-modified-date': None, 'address': [], 'path': '/0000-0000-0000-0000/address'}, 'keywords': {'last-modified-date': None, 'keyword': [], 'path': '/0000-0000-0000-0000/keywords'}, 'external-identifiers': {'last-modified-date': None, 'external-identifier': [], 'path': '/0000-0000-0000-0000/external-identifiers'}, 'path': '/0000-0000-0000-0000/person'}, 'activities-summary': {'last-modified-date': None, 'educations': {'last-modified-date': None, 'education-summary': [], 'path': '/0000-0000-0000-0000/educations'}, 'employments': {'last-modified-date': None, 'employment-summary': [], 'path': '/0000-0000-0000-0000/employments'}, 'fundings': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/fundings'}, 'peer-reviews': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/peer-reviews'}, 'works': {'last-modified-date': None, 'group': [], 'path': '/0000-0000-0000-0000/works'}, 'path': '/0000-0000-0000-0000/activities'}, 'path': '/0000-0000-0000-0000'} - @mock.patch('utils.orcid.get_orcid_record', return_value=all_fields) +class TestORCiDRecord(TestCase): + all_fields = { + "orcid-identifier": { + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "path": "0000-0000-0000-0000", + "host": "sandbox.orcid.org", + }, + "preferences": {"locale": "EN"}, + "history": { + "creation-method": "DIRECT", + "completion-date": None, + "submission-date": {"value": 1716899022299}, + "last-modified-date": {"value": 1717012729950}, + "claimed": True, + "source": None, + "deactivation-date": None, + "verified-email": True, + "verified-primary-email": True, + }, + "person": { + "last-modified-date": {"value": 1717012710380}, + "name": { + "created-date": {"value": 1716899022606}, + "last-modified-date": {"value": 1716931428927}, + "given-names": {"value": "cdleschol"}, + "family-name": {"value": "arship"}, + "credit-name": None, + "source": None, + "visibility": "PUBLIC", + "path": "0000-0000-0000-0000", + }, + "other-names": { + "last-modified-date": None, + "other-name": [], + "path": "/0000-0000-0000-0000/other-names", + }, + "biography": None, + "researcher-urls": { + "last-modified-date": None, + "researcher-url": [], + "path": "/0000-0000-0000-0000/researcher-urls", + }, + "emails": { + "last-modified-date": {"value": 1717012710380}, + "email": [ + { + "created-date": {"value": 1716899022599}, + "last-modified-date": {"value": 1717012710380}, + "source": { + "source-orcid": { + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "path": "0000-0000-0000-0000", + "host": "sandbox.orcid.org", + }, + "source-client-id": None, + "source-name": {"value": "cdleschol arship"}, + }, + "email": "cdleschol@mailinator.com", + "path": None, + "visibility": "PUBLIC", + "verified": True, + "primary": True, + "put-code": None, + } + ], + "path": "/0000-0000-0000-0000/email", + }, + "addresses": { + "last-modified-date": {"value": 1716931402191}, + "address": [ + { + "created-date": {"value": 1716931402191}, + "last-modified-date": {"value": 1716931402191}, + "source": { + "source-orcid": { + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "path": "0000-0000-0000-0000", + "host": "sandbox.orcid.org", + }, + "source-client-id": None, + "source-name": {"value": "cdleschol arship"}, + }, + "country": {"value": "US"}, + "visibility": "PUBLIC", + "path": "/0000-0000-0000-0000/address/7884", + "put-code": 7884, + "display-index": 1, + } + ], + "path": "/0000-0000-0000-0000/address", + }, + "keywords": { + "last-modified-date": None, + "keyword": [], + "path": "/0000-0000-0000-0000/keywords", + }, + "external-identifiers": { + "last-modified-date": None, + "external-identifier": [], + "path": "/0000-0000-0000-0000/external-identifiers", + }, + "path": "/0000-0000-0000-0000/person", + }, + "activities-summary": { + "last-modified-date": {"value": 1716931455651}, + "educations": { + "last-modified-date": None, + "education-summary": [], + "path": "/0000-0000-0000-0000/educations", + }, + "employments": { + "last-modified-date": {"value": 1716931455651}, + "employment-summary": [ + { + "created-date": {"value": 1716931455651}, + "last-modified-date": {"value": 1716931455651}, + "source": { + "source-orcid": { + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "path": "0000-0000-0000-0000", + "host": "sandbox.orcid.org", + }, + "source-client-id": None, + "source-name": {"value": "cdleschol arship"}, + }, + "department-name": None, + "role-title": None, + "start-date": None, + "end-date": None, + "organization": { + "name": "California Digital Library", + "address": { + "city": "Oakland", + "region": "California", + "country": "US", + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "https://ror.org/03yrm5c26", + "disambiguation-source": "ROR", + }, + }, + "visibility": "PUBLIC", + "put-code": 66225, + "path": "/0000-0000-0000-0000/employment/66225", + } + ], + "path": "/0000-0000-0000-0000/employments", + }, + "fundings": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/fundings", + }, + "peer-reviews": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/peer-reviews", + }, + "works": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/works", + }, + "path": "/0000-0000-0000-0000/activities", + }, + "path": "/0000-0000-0000-0000", + } + min_fields = { + "orcid-identifier": { + "uri": "http://sandbox.orcid.org/0000-0000-0000-0000", + "path": "0000-0000-0000-0000", + "host": "sandbox.orcid.org", + }, + "preferences": {"locale": "EN"}, + "history": { + "creation-method": "DIRECT", + "completion-date": None, + "submission-date": {"value": 1716899022299}, + "last-modified-date": {"value": 1717012843372}, + "claimed": True, + "source": None, + "deactivation-date": None, + "verified-email": True, + "verified-primary-email": True, + }, + "person": { + "last-modified-date": None, + "name": None, + "other-names": { + "last-modified-date": None, + "other-name": [], + "path": "/0000-0000-0000-0000/other-names", + }, + "biography": None, + "researcher-urls": { + "last-modified-date": None, + "researcher-url": [], + "path": "/0000-0000-0000-0000/researcher-urls", + }, + "emails": { + "last-modified-date": None, + "email": [], + "path": "/0000-0000-0000-0000/email", + }, + "addresses": { + "last-modified-date": None, + "address": [], + "path": "/0000-0000-0000-0000/address", + }, + "keywords": { + "last-modified-date": None, + "keyword": [], + "path": "/0000-0000-0000-0000/keywords", + }, + "external-identifiers": { + "last-modified-date": None, + "external-identifier": [], + "path": "/0000-0000-0000-0000/external-identifiers", + }, + "path": "/0000-0000-0000-0000/person", + }, + "activities-summary": { + "last-modified-date": None, + "educations": { + "last-modified-date": None, + "education-summary": [], + "path": "/0000-0000-0000-0000/educations", + }, + "employments": { + "last-modified-date": None, + "employment-summary": [], + "path": "/0000-0000-0000-0000/employments", + }, + "fundings": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/fundings", + }, + "peer-reviews": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/peer-reviews", + }, + "works": { + "last-modified-date": None, + "group": [], + "path": "/0000-0000-0000-0000/works", + }, + "path": "/0000-0000-0000-0000/activities", + }, + "path": "/0000-0000-0000-0000", + } + + @mock.patch("utils.orcid.get_orcid_record", return_value=all_fields) def test_record_details_all(self, mock_record): details = get_orcid_record_details("0000-0000-0000-0000") self.assertEqual(details["orcid"], "0000-0000-0000-0000") @@ -1227,7 +1491,7 @@ def test_record_details_all(self, mock_record): self.assertEqual(details["affiliation"], "California Digital Library") self.assertEqual(details["country"], "US") - @mock.patch('utils.orcid.get_orcid_record', return_value=min_fields) + @mock.patch("utils.orcid.get_orcid_record", return_value=min_fields) def test_record_details_min(self, mock_record): details = get_orcid_record_details("0000-0000-0000-0000") self.assertEqual(details["orcid"], "0000-0000-0000-0000") @@ -1239,7 +1503,12 @@ def test_record_details_min(self, mock_record): self.assertIsNone(details["country"]) def test_redirect_uri(self): - press= helpers.create_press() + press = helpers.create_press() repo = helpers.create_repository(press, [], []) - self.assertEqual(build_redirect_uri(repo), "http://localhost/login/orcid/?state=login") - self.assertEqual(build_redirect_uri(repo, action="register"), "http://localhost/login/orcid/?state=register") + self.assertEqual( + build_redirect_uri(repo), "http://localhost/login/orcid/?state=login" + ) + self.assertEqual( + build_redirect_uri(repo, action="register"), + "http://localhost/login/orcid/?state=register", + ) diff --git a/src/utils/transactional_emails.py b/src/utils/transactional_emails.py index cddb57e3b7..ac64c1d19c 100644 --- a/src/utils/transactional_emails.py +++ b/src/utils/transactional_emails.py @@ -22,11 +22,11 @@ def send_reviewer_withdrawl_notice(**kwargs): - review_assignment = kwargs['review_assignment'] - request = kwargs['request'] - email_data = kwargs['email_data'] + review_assignment = kwargs["review_assignment"] + request = kwargs["request"] + email_data = kwargs["email_data"] article = review_assignment.article - skip = kwargs.get('skip', True) + skip = kwargs.get("skip", True) description = '{0}\'s review of "{1}" has been withdrawn by {2}'.format( review_assignment.reviewer.full_name(), @@ -34,8 +34,10 @@ def send_reviewer_withdrawl_notice(**kwargs): request.user.full_name(), ) log_dict = { - 'level': 'Info', 'action_text': description, - 'types': 'Review Withdrawl', 'target': review_assignment.article, + "level": "Info", + "action_text": description, + "types": "Review Withdrawl", + "target": review_assignment.article, } if not skip: @@ -47,22 +49,22 @@ def send_reviewer_withdrawl_notice(**kwargs): log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_editor_unassigned_notice(request, email_data, assignment, skip=False): description = "{a.editor} unassigned from {a.article} by {r.user}".format( - a=assignment, - r=request, + a=assignment, + r=request, ) article = assignment.article if not skip: - log_dict = { - 'level': 'Info', 'action_text': description, - 'types': 'Editor Unassigned', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Editor Unassigned", + "target": article, } core_email.send_email( @@ -73,7 +75,7 @@ def send_editor_unassigned_notice(request, email_data, assignment, skip=False): log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_editor_assigned_acknowledgements_mandatory(**kwargs): @@ -85,22 +87,23 @@ def send_editor_assigned_acknowledgements_mandatory(**kwargs): :return: None """ - email_data = kwargs['email_data'] - editor_assignment = kwargs['editor_assignment'] + email_data = kwargs["email_data"] + editor_assignment = kwargs["editor_assignment"] article = editor_assignment.article - request = kwargs['request'] - skip = kwargs.get('skip', True) - acknowledgement = kwargs['acknowledgement'] + request = kwargs["request"] + skip = kwargs.get("skip", True) + acknowledgement = kwargs["acknowledgement"] description = '{0} was assigned as the editor for "{1}"'.format( - editor_assignment.editor.full_name(), - article.title + editor_assignment.editor.full_name(), article.title ) - log_dict = {'level': 'Info', - 'action_text': description, - 'types': 'Editor Assignment', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Editor Assignment", + "target": article, + } # send to assigned editor if not skip: @@ -114,11 +117,7 @@ def send_editor_assigned_acknowledgements_mandatory(**kwargs): # send to editor if not acknowledgement: - notify_helpers.send_slack( - request, - description, - ['slack_editors'] - ) + notify_helpers.send_slack(request, description, ["slack_editors"]) core_email.send_email( request.user.email, @@ -136,7 +135,7 @@ def send_editor_assigned_acknowledgements(**kwargs): :param kwargs: a list of kwargs that includes editor_assignment, user_message_content, skip (boolean) and request :return: None """ - kwargs['acknowledgement'] = True + kwargs["acknowledgement"] = True send_editor_assigned_acknowledgements_mandatory(**kwargs) @@ -149,9 +148,9 @@ def send_editor_manually_assigned(**kwargs): :return: None """ email_data = kwargs["email_data"] - editor_assignment = kwargs['editor_assignment'] + editor_assignment = kwargs["editor_assignment"] article = editor_assignment.article - request = kwargs['request'] + request = kwargs["request"] skip = kwargs.get("skip", True) # send to assigned editor @@ -168,7 +167,7 @@ def send_editor_manually_assigned(**kwargs): article.title, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_reviewer_requested(**kwargs): @@ -179,9 +178,9 @@ def send_reviewer_requested(**kwargs): :return: None """ email_data = kwargs["email_data"] - review_assignment = kwargs['review_assignment'] + review_assignment = kwargs["review_assignment"] article = review_assignment.article - request = kwargs['request'] + request = kwargs["request"] skip = kwargs.get("skip", True) description = 'A review request was added to "{0}" for user {1}'.format( @@ -189,10 +188,12 @@ def send_reviewer_requested(**kwargs): review_assignment.reviewer.full_name(), ) - log_dict = {'level': 'Info', - 'action_text': description, - 'types': 'Review Request', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Review Request", + "target": article, + } if not skip: core_email.send_email( @@ -203,7 +204,8 @@ def send_reviewer_requested(**kwargs): log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) + def send_reviewer_requested_acknowledgements(**kwargs): """ @@ -213,38 +215,40 @@ def send_reviewer_requested_acknowledgements(**kwargs): :return: None """ - review_assignment = kwargs['review_assignment'] + review_assignment = kwargs["review_assignment"] article = review_assignment.article - request = kwargs['request'] - user_message_content = kwargs['user_message_content'] + request = kwargs["request"] + user_message_content = kwargs["user_message_content"] - if 'skip' not in kwargs: - kwargs['skip'] = True + if "skip" not in kwargs: + kwargs["skip"] = True - skip = kwargs['skip'] + skip = kwargs["skip"] description = 'A review request was added to "{0}" for user {1}'.format( article.title, review_assignment.reviewer.full_name(), ) - log_dict = {'level': 'Info', - 'action_text': description, - 'types': 'Review Request', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Review Request", + "target": article, + } # send to requested reviewer if not skip: notify_helpers.send_email_with_body_from_user( request, - 'subject_review_assignment', + "subject_review_assignment", review_assignment.reviewer.email, user_message_content, log_dict=log_dict, ) # send slack - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_review_complete_acknowledgements(**kwargs): @@ -254,9 +258,9 @@ def send_review_complete_acknowledgements(**kwargs): :param kwargs: a list of kwargs that includes review_assignment, and request :return: None """ - review_assignment = kwargs['review_assignment'] + review_assignment = kwargs["review_assignment"] article = review_assignment.article - request = kwargs['request'] + request = kwargs["request"] request.user = review_assignment.reviewer description = '{0} completed the review of "{1}": {2}'.format( @@ -266,9 +270,9 @@ def send_review_complete_acknowledgements(**kwargs): ) util_models.LogEntry.add_entry( - types='Review Complete', + types="Review Complete", description=description, - level='Info', + level="Info", actor=request.user, target=article, request=request, @@ -276,51 +280,51 @@ def send_review_complete_acknowledgements(**kwargs): review_in_review_url = request.journal.site_url( path=reverse( - 'review_in_review', - kwargs={'article_id': article.pk}, + "review_in_review", + kwargs={"article_id": article.pk}, ) ) context = { - 'article': article, - 'request': request, - 'review_assignment': review_assignment, + "article": article, + "request": request, + "review_assignment": review_assignment, } # send slack - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) reviewer_log_dict = { - 'level': 'Info', - 'action_text': f"Review complete notification sent to {review_assignment.reviewer.full_name}", - 'types': 'Review Complete', - 'target': article, + "level": "Info", + "action_text": f"Review complete notification sent to {review_assignment.reviewer.full_name}", + "types": "Review Complete", + "target": article, } editor_log_dict = { - 'level': 'Info', - 'action_text': f"Review complete notification sent to {review_assignment.editor.full_name}", - 'types': 'Review Complete', - 'target': article, + "level": "Info", + "action_text": f"Review complete notification sent to {review_assignment.editor.full_name}", + "types": "Review Complete", + "target": article, } # send to reviewer notify_helpers.send_email_with_body_from_setting_template( request, - 'review_complete_reviewer_acknowledgement', - 'subject_review_complete_reviewer_acknowledgement', + "review_complete_reviewer_acknowledgement", + "subject_review_complete_reviewer_acknowledgement", review_assignment.reviewer.email, context, log_dict=reviewer_log_dict, ) # send to editor - context['review_in_review_url'] = review_in_review_url + context["review_in_review_url"] = review_in_review_url editors = get_assignment_editors(review_assignment) for editor in editors: notify_helpers.send_email_with_body_from_setting_template( request, - 'review_complete_acknowledgement', - 'subject_review_complete_acknowledgement', + "review_complete_acknowledgement", + "subject_review_complete_acknowledgement", editor.email, context, log_dict=editor_log_dict, @@ -334,21 +338,21 @@ def send_reviewer_accepted_or_decline_acknowledgements(**kwargs): :param kwargs: a list of kwargs that includes review_assignment, accepted and request :return: None """ - review_assignment = kwargs['review_assignment'] + review_assignment = kwargs["review_assignment"] article = review_assignment.article - request = kwargs['request'] - accepted = kwargs['accepted'] + request = kwargs["request"] + accepted = kwargs["accepted"] - description = '{0} {1} to review {2}'.format( + description = "{0} {1} to review {2}".format( review_assignment.reviewer.full_name(), - ('accepted' if accepted else 'declined'), + ("accepted" if accepted else "declined"), article.title, ) util_models.LogEntry.add_entry( - types='Review request {0}'.format(('accepted' if accepted else 'declined')), + types="Review request {0}".format(("accepted" if accepted else "declined")), description=description, - level='Info', + level="Info", actor=request.user, target=article, request=request, @@ -361,32 +365,32 @@ def send_reviewer_accepted_or_decline_acknowledgements(**kwargs): review_in_review_url = request.journal.site_url( path=reverse( - 'review_in_review', - kwargs={'article_id': article.pk}, + "review_in_review", + kwargs={"article_id": article.pk}, ) ) context = { - 'article': article, - 'request': request, - 'review_assignment': review_assignment, + "article": article, + "request": request, + "review_assignment": review_assignment, } reviewer_context = context - reviewer_context['review_url'] = review_url + reviewer_context["review_url"] = review_url editor_context = context - editor_context['review_in_review_url'] = review_in_review_url + editor_context["review_in_review_url"] = review_in_review_url # send to slack - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) # send to reviewer if accepted: context["reviewer_decision"] = _("accepted") notify_helpers.send_email_with_body_from_setting_template( request, - 'review_accept_acknowledgement', - 'subject_review_accept_acknowledgement', + "review_accept_acknowledgement", + "subject_review_accept_acknowledgement", review_assignment.reviewer.email, reviewer_context, ) @@ -395,8 +399,8 @@ def send_reviewer_accepted_or_decline_acknowledgements(**kwargs): context["reviewer_decision"] = _("declined") notify_helpers.send_email_with_body_from_setting_template( request, - 'review_decline_acknowledgement', - 'subject_review_decline_acknowledgement', + "review_decline_acknowledgement", + "subject_review_decline_acknowledgement", review_assignment.reviewer.email, reviewer_context, ) @@ -406,8 +410,8 @@ def send_reviewer_accepted_or_decline_acknowledgements(**kwargs): for editor in editors: notify_helpers.send_email_with_body_from_setting_template( request, - 'reviewer_acknowledgement', - 'subject_reviewer_acknowledgement', + "reviewer_acknowledgement", + "subject_reviewer_acknowledgement", editor.email, editor_context, ) @@ -422,50 +426,51 @@ def send_submission_acknowledgement(**kwargs): :return: None """ - article = kwargs['article'] - request = kwargs['request'] + article = kwargs["article"] + request = kwargs["request"] util_models.LogEntry.add_entry( - types='Submission Complete', - description='A new article {0} was submitted'.format(article.title), - level='Info', + types="Submission Complete", + description="A new article {0} was submitted".format(article.title), + level="Info", actor=request.user, target=article, request=request, ) log_dict = { - 'level': 'Info', - 'action_text': 'A new article {0} was submitted'.format(article.title), - 'types': 'New Submission Acknowledgement', - 'target': article, + "level": "Info", + "action_text": "A new article {0} was submitted".format(article.title), + "types": "New Submission Acknowledgement", + "target": article, } # generate URL review_unassigned_article_url = request.journal.site_url( path=reverse( - 'review_unassigned_article', - kwargs={'article_id': article.pk}, + "review_unassigned_article", + kwargs={"article_id": article.pk}, ) ) notify_helpers.send_slack( request, - 'New submission: {0} {1}'.format( + "New submission: {0} {1}".format( article.title, review_unassigned_article_url, ), - ['slack_editors']) + ["slack_editors"], + ) # send to author context = { - 'article': article, - 'request': request, - 'review_unassigned_article_url': review_unassigned_article_url, + "article": article, + "request": request, + "review_unassigned_article_url": review_unassigned_article_url, } notify_helpers.send_email_with_body_from_setting_template( request, - 'submission_acknowledgement', - 'subject_submission_acknowledgement', + "submission_acknowledgement", + "subject_submission_acknowledgement", article.correspondence_author.email, context, log_dict=log_dict, @@ -473,13 +478,15 @@ def send_submission_acknowledgement(**kwargs): # send to all editors editors_to_email = setting_handler.get_setting( - 'general', 'editors_for_notification', request.journal).processed_value + "general", "editors_for_notification", request.journal + ).processed_value if editors_to_email: editor_pks = [int(pk) for pk in editors_to_email] editor_emails = { - role.user.email for role in core_models.AccountRole.objects.filter( - role__slug='editor', + role.user.email + for role in core_models.AccountRole.objects.filter( + role__slug="editor", user__id__in=editor_pks, ) } @@ -487,42 +494,45 @@ def send_submission_acknowledgement(**kwargs): editor_emails = set(request.journal.editor_emails) assigned_to_section = ( - article.section.editors.all() | article.section.section_editors.all()) + article.section.editors.all() | article.section.section_editors.all() + ) editor_emails |= {editor.email for editor in assigned_to_section} notify_helpers.send_email_with_body_from_setting_template( request, - 'editor_new_submission', - 'subject_editor_new_submission', + "editor_new_submission", + "subject_editor_new_submission", editor_emails, context, log_dict=log_dict, - custom_reply_to=[f"noreply{settings.DUMMY_EMAIL_DOMAIN}"] + custom_reply_to=[f"noreply{settings.DUMMY_EMAIL_DOMAIN}"], ) def send_article_decision(**kwargs): - article = kwargs['article'] - request = kwargs['request'] - decision = kwargs['decision'] + article = kwargs["article"] + request = kwargs["request"] + decision = kwargs["decision"] - if 'skip' not in kwargs: - kwargs['skip'] = True + if "skip" not in kwargs: + kwargs["skip"] = True - skip = kwargs['skip'] + skip = kwargs["skip"] description = '{0}\'s article "{1}" has been {2}ed by {3}'.format( article.correspondence_author.full_name(), article.title, decision, - request.user.full_name() + request.user.full_name(), ) - log_dict = {'level': 'Info', - 'action_text': description, - 'types': 'Article Decision', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Article Decision", + "target": article, + } if not skip: core_email.send_email( @@ -533,35 +543,36 @@ def send_article_decision(**kwargs): log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_revisions_request(**kwargs): - request = kwargs['request'] - revision = kwargs['revision'] - user_message_content = kwargs['user_message_content'] + request = kwargs["request"] + revision = kwargs["revision"] + user_message_content = kwargs["user_message_content"] - if 'skip' not in kwargs: - kwargs['skip'] = True + if "skip" not in kwargs: + kwargs["skip"] = True - skip = kwargs['skip'] + skip = kwargs["skip"] - description = '{0} has requested revisions for {1} due on {2}'.format( + description = "{0} has requested revisions for {1} due on {2}".format( request.user.full_name(), revision.article.title, revision.date_due, ) - log_dict = {'level': 'Info', - 'action_text': description, - 'types': 'Revision Request', - 'target': revision.article, - } + log_dict = { + "level": "Info", + "action_text": description, + "types": "Revision Request", + "target": revision.article, + } if not skip: notify_helpers.send_email_with_body_from_user( request, - 'subject_request_revisions', + "subject_request_revisions", revision.article.correspondence_author.email, user_message_content, log_dict=log_dict, @@ -569,88 +580,92 @@ def send_revisions_request(**kwargs): notify_helpers.send_slack( request, description, - ['slack_editors'], + ["slack_editors"], ) def send_revisions_complete(**kwargs): - request = kwargs['request'] - revision = kwargs['revision'] + request = kwargs["request"] + revision = kwargs["revision"] article = revision.article - action_text = '' + action_text = "" for action in revision.actions.all(): - action_text = "{0}

{1} - {2}".format(action_text, action.logged, action.text) + action_text = "{0}

{1} - {2}".format( + action_text, action.logged, action.text + ) - description = ('

{0} has completed revisions for {1}

Actions:
{2}' - ''.format(request.user.full_name(), article.title, action_text) + description = ( + "

{0} has completed revisions for {1}

Actions:
{2}" "".format( + request.user.full_name(), article.title, action_text + ) ) url = request.journal.site_url( path=reverse( - 'view_revision', + "view_revision", kwargs={ - 'article_id': article.pk, - 'revision_id': revision.pk, + "article_id": article.pk, + "revision_id": revision.pk, }, ) ) # Get custom reply-to if se pii is enabled. se_pii_filter_enabled = request.journal.get_setting( - group_name='permission', - setting_name='se_pii_filter', + group_name="permission", + setting_name="se_pii_filter", ) custom_reply_to = None if se_pii_filter_enabled: custom_reply_to_value = request.journal.get_setting( - group_name='general', - setting_name='replyto_address', + group_name="general", + setting_name="replyto_address", ) if custom_reply_to_value: custom_reply_to = custom_reply_to_value notify_helpers.send_email_with_body_from_setting_template( request=request, - template='revisions_complete_editor_notification', - subject='subject_revisions_complete_editor_notification', + template="revisions_complete_editor_notification", + subject="subject_revisions_complete_editor_notification", to={editor.email for editor in get_assignment_editors(revision)}, context={ - 'request': request, - 'revision': revision, - 'url': url, + "request": request, + "revision": revision, + "url": url, }, log_dict={ - 'level': 'Info', - 'action_text': description, - 'types': 'Revisions Complete', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Revisions Complete", + "target": article, }, custom_reply_to=custom_reply_to, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_revisions_author_receipt(**kwargs): - request = kwargs['request'] - revision = kwargs['revision'] + request = kwargs["request"] + revision = kwargs["revision"] - description = '{0} has completed revisions for {1}'.format( + description = "{0} has completed revisions for {1}".format( request.user.full_name(), revision.article.title, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Revisions Complete', - 'target': revision.article, + "level": "Info", + "action_text": description, + "types": "Revisions Complete", + "target": revision.article, } context = { - 'revision': revision, + "revision": revision, } notify_helpers.send_email_with_body_from_setting_template( request, - 'revisions_complete_receipt', - 'subject_revisions_complete_receipt', + "revisions_complete_receipt", + "subject_revisions_complete_receipt", revision.article.correspondence_author.email, context, log_dict=log_dict, @@ -658,18 +673,18 @@ def send_revisions_author_receipt(**kwargs): notify_helpers.send_slack( request, description, - ['slack_editors'], + ["slack_editors"], ) def send_copyedit_assignment(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - article = kwargs['article'] + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + article = kwargs["article"] email_data = kwargs["email_data"] - skip = kwargs.get('skip', False) + skip = kwargs.get("skip", False) - description = '{0} has requested copyediting for {1} due on {2}'.format( + description = "{0} has requested copyediting for {1} due on {2}".format( request.user.full_name(), copyedit_assignment.article.title, copyedit_assignment.due, @@ -677,9 +692,10 @@ def send_copyedit_assignment(**kwargs): if not skip: log_dict = { - 'level': 'Info', 'action_text': description, - 'types': 'Copyedit Assignment', - 'target': copyedit_assignment.article, + "level": "Info", + "action_text": description, + "types": "Copyedit Assignment", + "target": copyedit_assignment.article, } core_email.send_email( copyedit_assignment.copyeditor, @@ -689,99 +705,124 @@ def send_copyedit_assignment(**kwargs): log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_copyedit_updated(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + skip = kwargs.get("skip", False) if not skip: # send to slack - notify_helpers.send_slack(request, - 'Copyedit assignment {0} updated'.format(copyedit_assignment.pk), - ['slack_editors']) + notify_helpers.send_slack( + request, + "Copyedit assignment {0} updated".format(copyedit_assignment.pk), + ["slack_editors"], + ) - log_dict = {'level': 'Info', - 'action_text': 'Copyedit assignment #{number} update.'.format(number=copyedit_assignment.pk), - 'types': 'Revision Request', - 'target': copyedit_assignment.article} + log_dict = { + "level": "Info", + "action_text": "Copyedit assignment #{number} update.".format( + number=copyedit_assignment.pk + ), + "types": "Revision Request", + "target": copyedit_assignment.article, + } # send to author - notify_helpers.send_email_with_body_from_setting_template(request, - 'copyedit_updated', - 'subject_copyedit_updated', - copyedit_assignment.copyeditor.email, - context={'request': request, - 'copyedit_assignment': copyedit_assignment}, - log_dict=log_dict) + notify_helpers.send_email_with_body_from_setting_template( + request, + "copyedit_updated", + "subject_copyedit_updated", + copyedit_assignment.copyeditor.email, + context={"request": request, "copyedit_assignment": copyedit_assignment}, + log_dict=log_dict, + ) def send_copyedit_deleted(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + skip = kwargs.get("skip", False) - description = 'Copyedit task {0} for article {1} deleted.'.format(copyedit_assignment.pk, - copyedit_assignment.article.title) + description = "Copyedit task {0} for article {1} deleted.".format( + copyedit_assignment.pk, copyedit_assignment.article.title + ) if not skip: # send to slack - notify_helpers.send_slack(request, - 'Copyedit assignment {0} updated'.format(copyedit_assignment.pk), - ['slack_editors']) + notify_helpers.send_slack( + request, + "Copyedit assignment {0} updated".format(copyedit_assignment.pk), + ["slack_editors"], + ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Copyedit Assignment Deleted', - 'target': copyedit_assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Copyedit Assignment Deleted", + "target": copyedit_assignment.article, + } # send to copyeditor - notify_helpers.send_email_with_body_from_setting_template(request, - 'copyedit_deleted', - 'subject_copyedit_deleted', - copyedit_assignment.copyeditor.email, - context={'request': request, - 'copyedit_assignment': copyedit_assignment}, - log_dict=log_dict) + notify_helpers.send_email_with_body_from_setting_template( + request, + "copyedit_deleted", + "subject_copyedit_deleted", + copyedit_assignment.copyeditor.email, + context={"request": request, "copyedit_assignment": copyedit_assignment}, + log_dict=log_dict, + ) def send_copyedit_decision(**kwargs): - request = kwargs['request'] + request = kwargs["request"] decision = kwargs["decision"] - copyedit_assignment = kwargs['copyedit_assignment'] + copyedit_assignment = kwargs["copyedit_assignment"] - description = '{0} has {1}ed copyediting task for {2} due on {3}.'.format( + description = "{0} has {1}ed copyediting task for {2} due on {3}.".format( copyedit_assignment.copyeditor.full_name(), decision, copyedit_assignment.article.title, - copyedit_assignment.due) + copyedit_assignment.due, + ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Copyediting Decision', - 'target': copyedit_assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Copyediting Decision", + "target": copyedit_assignment.article, + } - notify_helpers.send_email_with_body_from_user(request, 'subject_copyediting_decision', - copyedit_assignment.editor.email, - description, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "subject_copyediting_decision", + copyedit_assignment.editor.email, + description, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_copyedit_author_review(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - author_review = kwargs['author_review'] - email_data = kwargs['email_data'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + author_review = kwargs["author_review"] + email_data = kwargs["email_data"] + skip = kwargs.get("skip", False) - description = '{0} has requested copyedit review for {1} from {2}'.format( + description = "{0} has requested copyedit review for {1} from {2}".format( request.user.full_name(), copyedit_assignment.article.title, - copyedit_assignment.article.correspondence_author.full_name()) + copyedit_assignment.article.correspondence_author.full_name(), + ) if not skip: log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Copyedit Author Review', - 'target': copyedit_assignment.article, + "level": "Info", + "action_text": description, + "types": "Copyedit Author Review", + "target": copyedit_assignment.article, } core_email.send_email( @@ -793,60 +834,66 @@ def send_copyedit_author_review(**kwargs): ) author_review.notified = True author_review.save() - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_copyedit_complete(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - article = kwargs['article'] + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + article = kwargs["article"] - description = 'Copyediting requested by {0} from {1} for article {2} ' \ - 'has been completed'.format( + description = ( + "Copyediting requested by {0} from {1} for article {2} " + "has been completed".format( request.user.full_name(), copyedit_assignment.copyeditor.full_name(), - article.title + article.title, + ) ) log_dict = { - 'level': 'Info', 'action_text': description, - 'types': 'Copyedit Complete', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Copyedit Complete", + "target": article, } - article_copyediting_url = request.journal.site_url(reverse( - 'article_copyediting', args=[article.pk], - )) + article_copyediting_url = request.journal.site_url( + reverse( + "article_copyediting", + args=[article.pk], + ) + ) notify_helpers.send_email_with_body_from_setting_template( request, - 'copyeditor_notify_editor', - 'subject_copyeditor_notify_editor', + "copyeditor_notify_editor", + "subject_copyeditor_notify_editor", copyedit_assignment.editor.email, context={ - 'assignment': copyedit_assignment, - 'article_copyediting_url': article_copyediting_url, + "assignment": copyedit_assignment, + "article_copyediting_url": article_copyediting_url, }, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_author_copyedit_deleted(**kwargs): - request = kwargs.get('request') - author_review = kwargs.get('author_review') + request = kwargs.get("request") + author_review = kwargs.get("author_review") article = kwargs["article"] - email_data = kwargs.get('email_data') - skip = kwargs.get('skip', False) + email_data = kwargs.get("email_data") + skip = kwargs.get("skip", False) - description = '{0} has deleted a copyedit review for {1} from {2}'.format( + description = "{0} has deleted a copyedit review for {1} from {2}".format( request.user.full_name(), author_review.assignment.article.title, author_review.assignment.article.correspondence_author.full_name(), ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Author Copyedit Review Deleted', + "level": "Info", + "action_text": description, + "types": "Author Copyedit Review Deleted", } if not skip: @@ -859,467 +906,573 @@ def send_author_copyedit_deleted(**kwargs): ) util_models.LogEntry.add_entry( - 'Author Copyedit Review Deleted', + "Author Copyedit Review Deleted", description, - 'Info', + "Info", request.user, request, article, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_copyedit_ack(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - user_message_content = kwargs['user_message_content'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + user_message_content = kwargs["user_message_content"] + skip = kwargs.get("skip", False) - description = '{0} has acknowledged copyediting for {1}'.format(request.user.full_name(), - copyedit_assignment.article.title, ) + description = "{0} has acknowledged copyediting for {1}".format( + request.user.full_name(), + copyedit_assignment.article.title, + ) if not skip: - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Copyedit Acknowledgement', - 'target': copyedit_assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Copyedit Acknowledgement", + "target": copyedit_assignment.article, + } - notify_helpers.send_email_with_body_from_user(request, 'subject_copyeditor_ack', - copyedit_assignment.copyeditor.email, - user_message_content, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "subject_copyeditor_ack", + copyedit_assignment.copyeditor.email, + user_message_content, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_copyedit_reopen(**kwargs): - request = kwargs['request'] - copyedit_assignment = kwargs['copyedit_assignment'] - user_message_content = kwargs['user_message_content'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + copyedit_assignment = kwargs["copyedit_assignment"] + user_message_content = kwargs["user_message_content"] + skip = kwargs.get("skip", False) - description = '{0} has reopened copyediting for {1} from {2}'.format(request.user.full_name(), - copyedit_assignment.article.title, - copyedit_assignment.copyeditor.full_name()) + description = "{0} has reopened copyediting for {1} from {2}".format( + request.user.full_name(), + copyedit_assignment.article.title, + copyedit_assignment.copyeditor.full_name(), + ) if not skip: - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Copyedit Complete', - 'target': copyedit_assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Copyedit Complete", + "target": copyedit_assignment.article, + } - notify_helpers.send_email_with_body_from_user(request, 'subject_copyeditor_reopen_task', - copyedit_assignment.copyeditor.email, - user_message_content, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "subject_copyeditor_reopen_task", + copyedit_assignment.copyeditor.email, + user_message_content, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_typeset_assignment(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] - user_message_content = kwargs['user_message_content'] - skip = kwargs.get('skip', False) + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] + user_message_content = kwargs["user_message_content"] + skip = kwargs.get("skip", False) - description = '{0} has been assigned as a typesetter for {1}'.format(typeset_task.typesetter.full_name(), - typeset_task.assignment.article.title) + description = "{0} has been assigned as a typesetter for {1}".format( + typeset_task.typesetter.full_name(), typeset_task.assignment.article.title + ) if not skip: - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Typesetting Assignment', - 'target': typeset_task.assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Typesetting Assignment", + "target": typeset_task.assignment.article, + } - notify_helpers.send_email_with_body_from_user(request, 'subject_typesetter_notification', - typeset_task.typesetter.email, - user_message_content, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "subject_typesetter_notification", + typeset_task.typesetter.email, + user_message_content, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_typeset_decision(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] - decision = kwargs['decision'] + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] + decision = kwargs["decision"] - description = '{0} has {1}ed the typesetting task for {2}'.format(typeset_task.typesetter.full_name(), - decision, - typeset_task.assignment.article.title) + description = "{0} has {1}ed the typesetting task for {2}".format( + typeset_task.typesetter.full_name(), + decision, + typeset_task.assignment.article.title, + ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Typesetter Decision', - 'target': typeset_task.assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Typesetter Decision", + "target": typeset_task.assignment.article, + } - notify_helpers.send_email_with_body_from_user(request, 'Article Typesetting Decision', - typeset_task.assignment.production_manager.email, - description, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "Article Typesetting Decision", + typeset_task.assignment.production_manager.email, + description, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_typeset_task_deleted(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset'] + request = kwargs["request"] + typeset_task = kwargs["typeset"] - description = '{0} has deleted a typesetter task assigned to {1} for article {2}'.format( - request.user.full_name(), - typeset_task.typesetter.full_name(), - typeset_task.assignment.article.title, + description = ( + "{0} has deleted a typesetter task assigned to {1} for article {2}".format( + request.user.full_name(), + typeset_task.typesetter.full_name(), + typeset_task.assignment.article.title, + ) ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Typesetter Assignment Deleted', - 'target': typeset_task.assignment.article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Typesetter Assignment Deleted", + "target": typeset_task.assignment.article, + } # send to author - notify_helpers.send_email_with_body_from_setting_template(request, - 'typeset_deleted', - 'subject_typeset_deleted', - typeset_task.typesetter.email, - context={'request': request, - 'typeset_task': typeset_task}, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_setting_template( + request, + "typeset_deleted", + "subject_typeset_deleted", + typeset_task.typesetter.email, + context={"request": request, "typeset_task": typeset_task}, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_typeset_complete(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] - description = '{0} has completed typesetting for article {1}. \n\nThe following note was supplied:\n\n{2}'.format( + description = "{0} has completed typesetting for article {1}. \n\nThe following note was supplied:\n\n{2}".format( typeset_task.typesetter.full_name(), typeset_task.assignment.article.title, typeset_task.note_from_typesetter, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Typesetting Assignment Complete', - 'target': typeset_task.assignment.article, + "level": "Info", + "action_text": description, + "types": "Typesetting Assignment Complete", + "target": typeset_task.assignment.article, } production_article_url = request.journal.site_url( path=reverse( - 'production_article', - kwargs={'article_id': typeset_task.assignment.article.pk}, + "production_article", + kwargs={"article_id": typeset_task.assignment.article.pk}, ) ) context = { - 'production_article_url': production_article_url, - 'typeset_task': typeset_task, - 'production_assignment': typeset_task.assignment, + "production_article_url": production_article_url, + "typeset_task": typeset_task, + "production_assignment": typeset_task.assignment, } notify_helpers.send_email_with_body_from_setting_template( request, - 'typesetter_complete_notification', - 'subject_typesetter_complete_notification', + "typesetter_complete_notification", + "subject_typesetter_complete_notification", typeset_task.assignment.production_manager.email, context, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_production_complete(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - user_content_message = kwargs['user_content_message'] - assignment = kwargs['assignment'] + request = kwargs["request"] + article = kwargs["article"] + user_content_message = kwargs["user_content_message"] + assignment = kwargs["assignment"] - description = 'Production has been completed for article {0}.'.format(article.title) + description = "Production has been completed for article {0}.".format(article.title) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Production Complete', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Production Complete", + "target": article, } for task in assignment.typesettask_set.all(): notify_helpers.send_email_with_body_from_user( request, - 'Article Production Complete', + "Article Production Complete", task.typesetter.email, user_content_message, ) context = { - 'article': article, - 'assignment': assignment, + "article": article, + "assignment": assignment, } notify_helpers.send_email_with_body_from_setting_template( request, - 'production_complete', - 'subject_production_complete', + "production_complete", + "subject_production_complete", article.editor_emails(), context, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def fire_proofing_manager_assignment(**kwargs): - request = kwargs['request'] - proofing_assignment = kwargs['proofing_assignment'] + request = kwargs["request"] + proofing_assignment = kwargs["proofing_assignment"] article = proofing_assignment.article - description = '{0} has been assigned as proofing manager for {1}'.format( + description = "{0} has been assigned as proofing manager for {1}".format( proofing_assignment.proofing_manager.full_name(), article.title, ) log_dict = { - 'level': 'Info', 'action_text': description, - 'types': 'Proofing Manager Assigned', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Proofing Manager Assigned", + "target": article, } - proofing_url = request.journal.site_url(reverse( - 'proofing_article', args=[article.pk] - )) + proofing_url = request.journal.site_url( + reverse("proofing_article", args=[article.pk]) + ) context = { - 'request': request, - 'proofing_assignment': proofing_assignment, - 'article': article, - 'proofing_article_url': proofing_url, + "request": request, + "proofing_assignment": proofing_assignment, + "article": article, + "proofing_article_url": proofing_url, } notify_helpers.send_email_with_body_from_setting_template( request, - 'notify_proofing_manager', - 'subject_notify_proofing_manager', + "notify_proofing_manager", + "subject_notify_proofing_manager", proofing_assignment.proofing_manager.email, context, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def cancel_proofing_task(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - proofing_task = kwargs['proofing_task'] - user_content_message = kwargs.get('user_content_message', '') + request = kwargs["request"] + article = kwargs["article"] + proofing_task = kwargs["proofing_task"] + user_content_message = kwargs.get("user_content_message", "") - description = 'Proofing request for article {0} from {1} has been cancelled by {2}'.format( - article.title, - proofing_task.proofreader.full_name(), - request.user.full_name() + description = ( + "Proofing request for article {0} from {1} has been cancelled by {2}".format( + article.title, + proofing_task.proofreader.full_name(), + request.user.full_name(), + ) + ) + log_dict = { + "level": "Info", + "action_text": description, + "types": "Proofing Task Cancelled", + "target": article, + } + context = { + "request": request, + "proofing_task": proofing_task, + "user_content_message": user_content_message, + } + notify_helpers.send_email_with_body_from_setting_template( + request, + "notify_proofreader_cancelled", + "subject_notify_proofreader_cancelled", + proofing_task.proofreader.email, + context, + log_dict=log_dict, ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Proofing Task Cancelled', - 'target': article} - context = {'request': request, 'proofing_task': proofing_task, 'user_content_message': user_content_message} - notify_helpers.send_email_with_body_from_setting_template(request, - 'notify_proofreader_cancelled', - 'subject_notify_proofreader_cancelled', - proofing_task.proofreader.email, - context, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def edit_proofing_task(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - proofing_task = kwargs['proofing_task'] + request = kwargs["request"] + article = kwargs["article"] + proofing_task = kwargs["proofing_task"] - description = 'Proofing request for article {0} from {1} has been edited by {2}'.format( - article.title, - proofing_task.proofreader.full_name(), - request.user.full_name() + description = ( + "Proofing request for article {0} from {1} has been edited by {2}".format( + article.title, + proofing_task.proofreader.full_name(), + request.user.full_name(), + ) + ) + context = {"request": request, "proofing_task": proofing_task} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Proofing Task Edited", + "target": article, + } + notify_helpers.send_email_with_body_from_setting_template( + request, + "notify_proofreader_edited", + "subject_notify_proofreader_edited", + proofing_task.proofreader.email, + context, + log_dict=log_dict, ) - context = {'request': request, 'proofing_task': proofing_task} - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Proofing Task Edited', - 'target': article} - notify_helpers.send_email_with_body_from_setting_template(request, - 'notify_proofreader_edited', - 'subject_notify_proofreader_edited', - proofing_task.proofreader.email, - context, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def notify_proofreader(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - proofing_task = kwargs['proofing_task'] - user_content_message = kwargs['user_content_message'] + request = kwargs["request"] + article = kwargs["article"] + proofing_task = kwargs["proofing_task"] + user_content_message = kwargs["user_content_message"] - description = 'Proofing request for article {0} from {1} has been requested by {2}'.format( - article.title, - proofing_task.proofreader.full_name(), - request.user.full_name() + description = ( + "Proofing request for article {0} from {1} has been requested by {2}".format( + article.title, + proofing_task.proofreader.full_name(), + request.user.full_name(), + ) ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Proofreading Requested', - 'target': article} - notify_helpers.send_email_with_body_from_user(request, 'subject_notify_proofreader_assignment', - proofing_task.proofreader.email, - user_content_message, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + log_dict = { + "level": "Info", + "action_text": description, + "types": "Proofreading Requested", + "target": article, + } + notify_helpers.send_email_with_body_from_user( + request, + "subject_notify_proofreader_assignment", + proofing_task.proofreader.email, + user_content_message, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_proofreader_decision(**kwargs): - request = kwargs['request'] - proofing_task = kwargs['proofing_task'] - decision = kwargs['decision'] + request = kwargs["request"] + proofing_task = kwargs["proofing_task"] + decision = kwargs["decision"] - description = '{0} has made a decision for proofing task on {1}: {2}'.format( + description = "{0} has made a decision for proofing task on {1}: {2}".format( proofing_task.proofreader.full_name(), proofing_task.round.assignment.article.title, - decision + decision, + ) + log_dict = { + "level": "Info", + "action_text": description, + "types": "Proofreading Update", + "target": proofing_task.round.assignment.article, + } + notify_helpers.send_email_with_body_from_user( + request, + "Article Proofreading Update", + proofing_task.round.assignment.proofing_manager.email, + description, + log_dict=log_dict, ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Proofreading Update', - 'target': proofing_task.round.assignment.article} - notify_helpers.send_email_with_body_from_user(request, 'Article Proofreading Update', - proofing_task.round.assignment.proofing_manager.email, - description, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_proofreader_complete_notification(**kwargs): - request = kwargs['request'] - proofing_task = kwargs['proofing_task'] - article = kwargs['article'] + request = kwargs["request"] + proofing_task = kwargs["proofing_task"] + article = kwargs["article"] - description = '{0} has completed a proofing task for {1}'.format( + description = "{0} has completed a proofing task for {1}".format( proofing_task.proofreader.full_name(), article.title, ) - proofing_url = request.journal.site_url(reverse( - 'proofing_article', args=[article.pk] - )) + proofing_url = request.journal.site_url( + reverse("proofing_article", args=[article.pk]) + ) context = { - 'proofing_task': proofing_task, - 'proofing_article_url': proofing_url, - + "proofing_task": proofing_task, + "proofing_article_url": proofing_url, } notify_helpers.send_email_with_body_from_setting_template( request, - 'notify_proofreader_complete', - 'subject_notify_proofreader_complete', + "notify_proofreader_complete", + "subject_notify_proofreader_complete", proofing_task.round.assignment.proofing_manager.email, context, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_proofing_typeset_request(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] - article = kwargs['article'] - user_content_message = kwargs['user_content_message'] - skip = kwargs['skip'] + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] + article = kwargs["article"] + user_content_message = kwargs["user_content_message"] + skip = kwargs["skip"] - description = '{0} has requested typesetting updates from {1} for {2}'.format( + description = "{0} has requested typesetting updates from {1} for {2}".format( request.user.full_name(), typeset_task.typesetter.full_name(), article.title, ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Typesetting Updates Requested', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Typesetting Updates Requested", + "target": article, + } if not skip: - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) notify_helpers.send_email_with_body_from_user( - request, 'subject_notify_typesetter_proofing_changes', + request, + "subject_notify_typesetter_proofing_changes", typeset_task.typesetter.email, - user_content_message, log_dict=log_dict) + user_content_message, + log_dict=log_dict, + ) def send_proofing_typeset_decision(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] - decision = kwargs['decision'] + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] + decision = kwargs["decision"] - description = '{0} has made a decision for proofing task on {1}: {2}'.format( + description = "{0} has made a decision for proofing task on {1}: {2}".format( typeset_task.typesetter.full_name(), typeset_task.proofing_task.round.assignment.article.title, - decision + decision, ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Proofing Typesetting', - 'target': typeset_task.proofing_task.round.assignment.article} - notify_helpers.send_email_with_body_from_user(request, 'Proofing Typesetting Changes', - typeset_task.proofing_task.round.assignment.proofing_manager.email, - description, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + log_dict = { + "level": "Info", + "action_text": description, + "types": "Proofing Typesetting", + "target": typeset_task.proofing_task.round.assignment.article, + } + notify_helpers.send_email_with_body_from_user( + request, + "Proofing Typesetting Changes", + typeset_task.proofing_task.round.assignment.proofing_manager.email, + description, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_corrections_complete(**kwargs): - request = kwargs['request'] - typeset_task = kwargs['typeset_task'] - article = kwargs['article'] + request = kwargs["request"] + typeset_task = kwargs["typeset_task"] + article = kwargs["article"] - description = '{0} has completed corrections task for article {1} (proofing task {2})'.format( - request.user.full_name(), - article.title, - typeset_task.pk, + description = ( + "{0} has completed corrections task for article {1} (proofing task {2})".format( + request.user.full_name(), + article.title, + typeset_task.pk, + ) ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Proofing Typesetting Complete', - 'target': typeset_task.proofing_task.round.assignment.article, + "level": "Info", + "action_text": description, + "types": "Proofing Typesetting Complete", + "target": typeset_task.proofing_task.round.assignment.article, } proofing_article_url = request.journal.site_url( path=reverse( - 'production_article', - kwargs={'article_id': typeset_task.proofing_task.assignment.article.pk}, + "production_article", + kwargs={"article_id": typeset_task.proofing_task.assignment.article.pk}, ) ) context = { - 'typeset_task': typeset_task, - 'proofing_article_url': proofing_article_url, - 'production_assignment': typeset_task.proofing_task.assignment, + "typeset_task": typeset_task, + "proofing_article_url": proofing_article_url, + "production_assignment": typeset_task.proofing_task.assignment, } notify_helpers.send_email_with_body_from_setting_template( request, - 'typesetter_corrections_complete', - 'subject_typesetter_corrections_complete', + "typesetter_corrections_complete", + "subject_typesetter_corrections_complete", article.proofingassignment.proofing_manager.email, context, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_proofing_ack(**kwargs): - request = kwargs['request'] - user_message = kwargs['user_message'] - article = kwargs['article'] - model_object = kwargs['model_object'] - model_name = kwargs['model_name'] - skip = kwargs['skip'] - - description = "{0} has acknowledged a task , {1}, by {2} for article {3}".format(request.user, - model_name, - model_object.actor().full_name(), - article.title) + request = kwargs["request"] + user_message = kwargs["user_message"] + article = kwargs["article"] + model_object = kwargs["model_object"] + model_name = kwargs["model_name"] + skip = kwargs["skip"] + + description = "{0} has acknowledged a task , {1}, by {2} for article {3}".format( + request.user, model_name, model_object.actor().full_name(), article.title + ) if not skip: - notify_helpers.send_email_with_body_from_user(request, 'Proofing Acknowledgement', - model_object.actor().email, - user_message) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "Proofing Acknowledgement", + model_object.actor().email, + user_message, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_proofing_complete(**kwargs): - request = kwargs['request'] - user_message = kwargs['user_message'] - article = kwargs['article'] - skip = kwargs['skip'] + request = kwargs["request"] + user_message = kwargs["user_message"] + article = kwargs["article"] + skip = kwargs["skip"] description = "Proofing is now complete for {0}".format(article.title) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Proofing Complete', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Proofing Complete", + "target": article, } if not skip: notify_helpers.send_email_with_body_from_user( request, - 'subject_notify_editor_proofing_complete', + "subject_notify_editor_proofing_complete", article.editor_emails(), user_message, log_dict=log_dict, ) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) def send_prepub_notifications(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - formset = kwargs['formset'] + request = kwargs["request"] + article = kwargs["article"] + formset = kwargs["formset"] description = """ {article.title} was set to be published {article.date_published} @@ -1327,13 +1480,13 @@ def send_prepub_notifications(**kwargs): """ log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Article Set for Publication', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Article Set for Publication", + "target": article, } - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) for form in formset: core_email.send_email( @@ -1346,89 +1499,108 @@ def send_prepub_notifications(**kwargs): def send_author_publication_notification(**kwargs): - request = kwargs['request'] - article = kwargs['article'] - user_message = kwargs['user_message'] - section_editors = kwargs['section_editors'] - peer_reviewers = kwargs['peer_reviewers'] + request = kwargs["request"] + article = kwargs["article"] + user_message = kwargs["user_message"] + section_editors = kwargs["section_editors"] + peer_reviewers = kwargs["peer_reviewers"] - description = "Article, {0}, set for publication on {1}, by {2}".format(article.title, - article.date_published, - request.user.full_name()) + description = "Article, {0}, set for publication on {1}, by {2}".format( + article.title, article.date_published, request.user.full_name() + ) - log_dict = {'level': 'Info', 'action_text': description, 'types': 'Article Published', - 'target': article} + log_dict = { + "level": "Info", + "action_text": description, + "types": "Article Published", + "target": article, + } - notify_helpers.send_email_with_body_from_user(request, - 'subject_author_publication', - article.correspondence_author.email, - user_message, log_dict=log_dict) - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_email_with_body_from_user( + request, + "subject_author_publication", + article.correspondence_author.email, + user_message, + log_dict=log_dict, + ) + notify_helpers.send_slack(request, description, ["slack_editors"]) # Check for SEs and PRs and notify them as well if section_editors: for editor in article.section_editors(): notify_helpers.send_email_with_body_from_setting_template( request, - 'section_editor_pub_notification', - 'subject_section_editor_pub_notification', + "section_editor_pub_notification", + "subject_section_editor_pub_notification", editor.email, - {'article': article, 'editor': editor}, + {"article": article, "editor": editor}, ) if peer_reviewers: - reviewers = {review_assignment.reviewer for review_assignment in article.completed_reviews_with_decision} + reviewers = { + review_assignment.reviewer + for review_assignment in article.completed_reviews_with_decision + } for reviewer in reviewers: notify_helpers.send_email_with_body_from_setting_template( request, - 'peer_reviewer_pub_notification', - 'subject_peer_reviewer_pub_notification', + "peer_reviewer_pub_notification", + "subject_peer_reviewer_pub_notification", reviewer.email, - {'article': article, 'reviewer': reviewer}, + {"article": article, "reviewer": reviewer}, ) def review_sec_override_notification(**kwargs): - request = kwargs['request'] - override = kwargs['override'] + request = kwargs["request"] + override = kwargs["override"] - description = "{0} overrode their access to {1}".format(override.editor.full_name(), override.article.title) - log_dict = {'level': 'Warning', 'action_text': description, 'types': 'Security Override', - 'target': override.article} - notify_helpers.send_slack(request, description, ['slack_editors']) - notify_helpers.send_email_with_body_from_user(request, 'Review Security Override', - request.journal.editor_emails, - description, log_dict=log_dict) + description = "{0} overrode their access to {1}".format( + override.editor.full_name(), override.article.title + ) + log_dict = { + "level": "Warning", + "action_text": description, + "types": "Security Override", + "target": override.article, + } + notify_helpers.send_slack(request, description, ["slack_editors"]) + notify_helpers.send_email_with_body_from_user( + request, + "Review Security Override", + request.journal.editor_emails, + description, + log_dict=log_dict, + ) def send_draft_decison(**kwargs): - request = kwargs['request'] - draft = kwargs['draft'] - article = kwargs['article'] + request = kwargs["request"] + draft = kwargs["draft"] + article = kwargs["article"] description = "Section Editor {0} has drafted a decision for Article {1}".format( - draft.section_editor.full_name(), article.title) + draft.section_editor.full_name(), article.title + ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Draft Decision', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Draft Decision", + "target": article, } review_edit_draft_decision_url = request.journal.site_url( - path=reverse( - 'review_edit_draft_decision', args=[article.pk, draft.pk] - ) + path=reverse("review_edit_draft_decision", args=[article.pk, draft.pk]) ) context = { - 'draft': draft, - 'article': article, - 'review_edit_draft_decision_url': review_edit_draft_decision_url, + "draft": draft, + "article": article, + "review_edit_draft_decision_url": review_edit_draft_decision_url, } - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) notify_helpers.send_email_with_body_from_setting_template( request, - 'draft_editor_message', - 'subject_draft_editor_message', + "draft_editor_message", + "subject_draft_editor_message", draft.editor.email if draft.editor else request.journal.editor_emails, context, log_dict=log_dict, @@ -1436,33 +1608,35 @@ def send_draft_decison(**kwargs): def send_author_copyedit_complete(**kwargs): - request = kwargs['request'] - copyedit = kwargs['copyedit'] - author_review = kwargs['author_review'] + request = kwargs["request"] + copyedit = kwargs["copyedit"] + author_review = kwargs["author_review"] editor_review_url = request.journal.site_url( path=reverse( - 'editor_review', + "editor_review", kwargs={ - 'article_id': copyedit.article.pk, - 'copyedit_id': copyedit.pk, - } + "article_id": copyedit.article.pk, + "copyedit_id": copyedit.pk, + }, ) ) - description = "Author {0} has completed their copyediting task for article {1}".format( - author_review.author.full_name(), - copyedit.article.title, + description = ( + "Author {0} has completed their copyediting task for article {1}".format( + author_review.author.full_name(), + copyedit.article.title, + ) ) context = { - 'copyedit': copyedit, - 'author_review': author_review, - 'editor_review_url': editor_review_url, + "copyedit": copyedit, + "author_review": author_review, + "editor_review_url": editor_review_url, } - notify_helpers.send_slack(request, description, ['slack_editors']) + notify_helpers.send_slack(request, description, ["slack_editors"]) notify_helpers.send_email_with_body_from_setting_template( request, - 'author_copyedit_complete', - 'subject_author_copyedit_complete', + "author_copyedit_complete", + "subject_author_copyedit_complete", copyedit.editor.email, context, ) @@ -1475,23 +1649,23 @@ def preprint_submission(**kwargs): :param kwargs: Dictionary containing article and request objects :return: None """ - request = kwargs.get('request') - preprint = kwargs.get('preprint') + request = kwargs.get("request") + preprint = kwargs.get("preprint") - description = '{author} has submitted a new {obj} titled {title}.'.format( + description = "{author} has submitted a new {obj} titled {title}.".format( author=request.user.full_name(), obj=request.repository.object_name, title=preprint.title, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Submission', - 'target': preprint, + "level": "Info", + "action_text": description, + "types": "Submission", + "target": preprint, } # Send an email to the user - context = {'preprint': preprint} + context = {"preprint": preprint} template = request.repository.submission email_text = render_template.get_message_content( request, @@ -1501,7 +1675,7 @@ def preprint_submission(**kwargs): ) notify_helpers.send_email_with_body_from_user( request, - '{} Submission'.format(request.repository.object_name), + "{} Submission".format(request.repository.object_name), request.user.email, email_text, log_dict=log_dict, @@ -1509,21 +1683,25 @@ def preprint_submission(**kwargs): # Send an email to the preprint editor url = request.repository.site_url() + reverse( - 'repository_manager_article', - kwargs={'preprint_id': preprint.pk}, + "repository_manager_article", + kwargs={"preprint_id": preprint.pk}, ) editor_email_text = 'A new {object} has been submitted to {press}: {title}.'.format( object=request.repository.object_name, press=request.repository.name, url=url, - title=preprint.title + title=preprint.title, ) repo = request.repository - recipients = repo.submission_notification_recipients if repo.submission_notification_recipients.count() > 0 else repo.managers + recipients = ( + repo.submission_notification_recipients + if repo.submission_notification_recipients.count() > 0 + else repo.managers + ) for r in recipients.all(): notify_helpers.send_email_with_body_from_user( request, - '{} Submission'.format(request.repository.object_name), + "{} Submission".format(request.repository.object_name), r.email, editor_email_text, log_dict=log_dict, @@ -1536,36 +1714,36 @@ def preprint_notification(**kwargs): :param kwargs: Dict with preprint, content and request objects :return: None """ - request = kwargs.get('request') - preprint = kwargs.get('preprint') - content = kwargs.get('email_content') - skip = kwargs.get('skip') + request = kwargs.get("request") + preprint = kwargs.get("preprint") + content = kwargs.get("email_content") + skip = kwargs.get("skip") if preprint.date_declined: - types = 'Rejected' - description = '

{editor} has rejected \'{title}\'. Moderator reason:

{reason}

'.format( + types = "Rejected" + description = "

{editor} has rejected '{title}'. Moderator reason:

{reason}

".format( editor=request.user.full_name(), title=preprint.title, reason=preprint.preprint_decline_note, ) else: - types = 'Accepted' - description = '{editor} has published \'{title}\'.'.format( + types = "Accepted" + description = "{editor} has published '{title}'.".format( editor=request.user.full_name(), title=preprint.title, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': types, - 'target': preprint, + "level": "Info", + "action_text": description, + "types": types, + "target": preprint, } util_models.LogEntry.add_entry( types, description, - 'Info', + "Info", request.user, request, preprint, @@ -1574,7 +1752,7 @@ def preprint_notification(**kwargs): if not skip: notify_helpers.send_email_with_body_from_user( request, - '{} Submission Decision'.format(preprint.title), + "{} Submission Decision".format(preprint.title), preprint.owner.email, content, log_dict=log_dict, @@ -1586,35 +1764,37 @@ def preprint_notification(**kwargs): def preprint_comment(**kwargs): - request = kwargs.get('request') - preprint = kwargs.get('preprint') + request = kwargs.get("request") + preprint = kwargs.get("preprint") path = reverse( - 'repository_comments', - kwargs={'preprint_id': preprint.pk}, + "repository_comments", + kwargs={"preprint_id": preprint.pk}, ) url = request.repository.site_url(path) - email_text = 'A comment has been made on your article {title}, you can moderate comments ' \ - 'on the journal site.'.format( - title=preprint.title, - url=url, + email_text = ( + "A comment has been made on your article {title}, you can moderate comments " + 'on the journal site.'.format( + title=preprint.title, + url=url, + ) ) - description = '{author} commented on {title}'.format( + description = "{author} commented on {title}".format( author=request.user.full_name(), title=preprint.title, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Preprint Comment', - 'target': preprint, + "level": "Info", + "action_text": description, + "types": "Preprint Comment", + "target": preprint, } notify_helpers.send_email_with_body_from_user( request, - 'Preprint Comment', + "Preprint Comment", preprint.owner.email, email_text, log_dict=log_dict, @@ -1622,30 +1802,30 @@ def preprint_comment(**kwargs): def preprint_version_update(**kwargs): - request = kwargs.get('request') - pending_update = kwargs.get('pending_update') - action = kwargs.get('action') - reason = kwargs.get('reason') + request = kwargs.get("request") + pending_update = kwargs.get("pending_update") + action = kwargs.get("action") + reason = kwargs.get("reason") - description = '{object} Pending Version {pk}: Decision: {decision}'.format( + description = "{object} Pending Version {pk}: Decision: {decision}".format( object=request.repository.object_name, pk=pending_update.pk, decision=action, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Preprint Publication', - 'target': pending_update.preprint, + "level": "Info", + "action_text": description, + "types": "Preprint Publication", + "target": pending_update.preprint, } context = { - 'pending_update': pending_update, - 'reason': reason, + "pending_update": pending_update, + "reason": reason, } - if action == 'accept': + if action == "accept": template = request.repository.accept_version email_text = render_template.get_message_content( request, @@ -1663,7 +1843,7 @@ def preprint_version_update(**kwargs): ) notify_helpers.send_email_with_body_from_user( request, - '{} Version Update'.format(pending_update.preprint.title), + "{} Version Update".format(pending_update.preprint.title), pending_update.preprint.owner.email, email_text, log_dict=log_dict, @@ -1671,26 +1851,26 @@ def preprint_version_update(**kwargs): def send_cancel_corrections(**kwargs): - request = kwargs.get('request') - article = kwargs.get('article') - correction = kwargs.get('correction') + request = kwargs.get("request") + article = kwargs.get("article") + correction = kwargs.get("correction") - description = '{user} has cancelled correction task {task}'.format( + description = "{user} has cancelled correction task {task}".format( user=request.user, task=correction, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Correction Cancelled', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Correction Cancelled", + "target": article, } notify_helpers.send_email_with_body_from_setting_template( request, - 'notify_correction_cancelled', - 'subject_notify_correction_cancelled', + "notify_correction_cancelled", + "subject_notify_correction_cancelled", correction.typesetter.email, context=kwargs, log_dict=log_dict, @@ -1698,7 +1878,7 @@ def send_cancel_corrections(**kwargs): def get_assignment_editors(assignment): - """ Get editors relevant to a review or revision assignment + """Get editors relevant to a review or revision assignment This is a helper function to retrieve the editors that should be notified of changes in a review/ revision assignment. @@ -1720,27 +1900,27 @@ def get_assignment_editors(assignment): def send_draft_decision_declined(**kwargs): - request = kwargs.get('request') - article = kwargs.get('article') - draft_decision = kwargs.get('draft_decision') + request = kwargs.get("request") + article = kwargs.get("article") + draft_decision = kwargs.get("draft_decision") - description = '{user} has declined a draft decision {draft} written by {section_editor}'.format( + description = "{user} has declined a draft decision {draft} written by {section_editor}".format( user=request.user, draft=draft_decision.pk, section_editor=draft_decision.section_editor.full_name, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Draft Decision Declined', - 'target': article, + "level": "Info", + "action_text": description, + "types": "Draft Decision Declined", + "target": article, } notify_helpers.send_email_with_body_from_setting_template( request, - 'notify_se_draft_declined', - 'subject_notify_se_draft_declined', + "notify_se_draft_declined", + "subject_notify_se_draft_declined", draft_decision.section_editor.email, context=kwargs, log_dict=log_dict, @@ -1748,86 +1928,88 @@ def send_draft_decision_declined(**kwargs): def access_request_notification(**kwargs): - request = kwargs.get('request') - access_request = kwargs.get('access_request') - description = '{} has requested the {} role for {}'.format( + request = kwargs.get("request") + access_request = kwargs.get("access_request") + description = "{} has requested the {} role for {}".format( request.user, access_request.role.name, request.site_type.name, ) if request.journal: - contact = request.journal.get_setting('general', 'submission_access_request_contact') + contact = request.journal.get_setting( + "general", "submission_access_request_contact" + ) else: contact = request.repository.submission_access_contact log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Access Request', - 'target': request.site_type, + "level": "Info", + "action_text": description, + "types": "Access Request", + "target": request.site_type, } if contact: notify_helpers.send_email_with_body_from_setting_template( request, - 'submission_access_request_notification', - 'subject_submission_access_request_notification', + "submission_access_request_notification", + "subject_submission_access_request_notification", contact, - context={'description': description}, + context={"description": description}, log_dict=log_dict, ) def access_request_complete(**kwargs): - request = kwargs.get('request') - access_request = kwargs.get('access_request') - decision = kwargs.get('decision') + request = kwargs.get("request") + access_request = kwargs.get("access_request") + decision = kwargs.get("decision") description = "Access request from {} evaluated by {}: {}".format( access_request.user.full_name, request.user, decision, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Access Request', - 'target': request.site_type, + "level": "Info", + "action_text": description, + "types": "Access Request", + "target": request.site_type, } notify_helpers.send_email_with_body_from_setting_template( request, - 'submission_access_request_complete', - 'subject_submission_access_request_complete', + "submission_access_request_complete", + "subject_submission_access_request_complete", access_request.user.email, context={ - 'access_request': access_request, - 'decision': decision, + "access_request": access_request, + "decision": decision, }, log_dict=log_dict, ) def preprint_review_notification(**kwargs): - request = kwargs.get('request') - preprint = kwargs.get('preprint') - review = kwargs.get('review') - message = kwargs.get('message') - skip = kwargs.get('skip', None) + request = kwargs.get("request") + preprint = kwargs.get("preprint") + review = kwargs.get("review") + message = kwargs.get("message") + skip = kwargs.get("skip", None) if not skip: - description = 'Review of {} requested from {} by {}.'.format( + description = "Review of {} requested from {} by {}.".format( preprint.title, review.reviewer.full_name(), review.manager.full_name(), ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Review', - 'target': preprint, + "level": "Info", + "action_text": description, + "types": "Review", + "target": preprint, } notify_helpers.send_email_with_body_from_user( request, - '{} Review Invitation'.format(request.repository.object_name), + "{} Review Invitation".format(request.repository.object_name), review.reviewer.email, message, log_dict=log_dict, @@ -1835,9 +2017,9 @@ def preprint_review_notification(**kwargs): def preprint_review_status_change(**kwargs): - request = kwargs.get('request') - review = kwargs.get('review') - status_change = kwargs.get('status_change') + request = kwargs.get("request") + review = kwargs.get("review") + status_change = kwargs.get("status_change") status_text = None description = "Status of review {} by {} is now: {}".format( @@ -1846,35 +2028,34 @@ def preprint_review_status_change(**kwargs): status_change, ) log_dict = { - 'level': 'Info', - 'action_text': description, - 'types': 'Review', - 'target': review.preprint, + "level": "Info", + "action_text": description, + "types": "Review", + "target": review.preprint, } - if status_change in ['accept', 'decline', 'complete']: + if status_change in ["accept", "decline", "complete"]: to = review.manager.email - if status_change == 'accept': - status_text = 'The reviewer has agreed to add a comment.' - elif status_change == 'decline': - status_text = 'The reviewer has declined to add a comment.' - elif status_change == 'complete': - status_text = 'The reviewer has submitted their comment.' + if status_change == "accept": + status_text = "The reviewer has agreed to add a comment." + elif status_change == "decline": + status_text = "The reviewer has declined to add a comment." + elif status_change == "complete": + status_text = "The reviewer has submitted their comment." template = request.repository.manager_review_status_change else: # withdraw to = review.reviewer.email template = request.repository.reviewer_review_status_change context = { - 'review': review, - 'status_text': status_text, - 'url': request.repository.site_url(path=reverse( - 'repository_review_detail', - kwargs={ - 'preprint_id': review.preprint.pk, - 'review_id': review.pk - } - )) + "review": review, + "status_text": status_text, + "url": request.repository.site_url( + path=reverse( + "repository_review_detail", + kwargs={"preprint_id": review.preprint.pk, "review_id": review.pk}, + ) + ), } email_text = render_template.get_message_content( request, @@ -1884,7 +2065,7 @@ def preprint_review_status_change(**kwargs): ) notify_helpers.send_email_with_body_from_user( request, - '{} Review Invitation Status'.format(request.repository.object_name), + "{} Review Invitation Status".format(request.repository.object_name), to, email_text, log_dict=log_dict, diff --git a/src/utils/upgrade/12_13.py b/src/utils/upgrade/12_13.py index 7b37dda9c0..7fb31cf625 100644 --- a/src/utils/upgrade/12_13.py +++ b/src/utils/upgrade/12_13.py @@ -9,10 +9,14 @@ SETTINGS_TO_CHANGE = [ - {'group': 'email', 'name': 'copyeditor_reopen_task', 'action': 'update'}, - {'group': 'email', 'name': 'author_copyedit_complete', 'action': 'update'}, - {'group': 'email', 'name': 'production_manager_notification', 'action': 'update'}, - {'group': 'email', 'name': 'review_complete_reviewer_acknowledgement', 'action': 'update'}, + {"group": "email", "name": "copyeditor_reopen_task", "action": "update"}, + {"group": "email", "name": "author_copyedit_complete", "action": "update"}, + {"group": "email", "name": "production_manager_notification", "action": "update"}, + { + "group": "email", + "name": "review_complete_reviewer_acknowledgement", + "action": "update", + }, ] @@ -22,15 +26,18 @@ def run_journal_signals(): so we want them to be fired on upgrade. :return: None """ - print('Processing journal signals') + print("Processing journal signals") journals = journal_models.Journal.objects.all() for journal in journals: - print('Firing signals for {journal_code}'.format(journal_code=journal.code), end='...') + print( + "Firing signals for {journal_code}".format(journal_code=journal.code), + end="...", + ) post_save.send(journal_models.Journal, instance=journal, created=True) - print(' [OK]') + print(" [OK]") def add_workflow_log_entries(article, stage_log_objects): @@ -41,22 +48,31 @@ def add_workflow_log_entries(article, stage_log_objects): :return: None """ - non_workflow_stages = ['Published', 'Assigned', 'Under Revision', - 'Author Copyediting', 'Final Copyediting', 'Rejected', 'Accepted'] + non_workflow_stages = [ + "Published", + "Assigned", + "Under Revision", + "Author Copyediting", + "Final Copyediting", + "Rejected", + "Accepted", + ] for entry in stage_log_objects: - - if entry.stage_to == 'Under Review': - stage = 'Unassigned' + if entry.stage_to == "Under Review": + stage = "Unassigned" else: stage = entry.stage_to if entry.stage_to not in non_workflow_stages: - workflow_element = core_models.WorkflowElement.objects.get(journal=article.journal, - stage=stage) - core_models.WorkflowLog.objects.get_or_create(article=article, - element=workflow_element, - defaults={'timestamp': entry.date_time}) + workflow_element = core_models.WorkflowElement.objects.get( + journal=article.journal, stage=stage + ) + core_models.WorkflowLog.objects.get_or_create( + article=article, + element=workflow_element, + defaults={"timestamp": entry.date_time}, + ) def process_article_workflow(): @@ -64,17 +80,19 @@ def process_article_workflow(): Processes an article and adds workflow history objects for it. :return: None """ - print('Processing workflow migration') + print("Processing workflow migration") for journal in journal_models.Journal.objects.all(): - print('Working on {journal_code}'.format(journal_code=journal.code), end='...') + print("Working on {journal_code}".format(journal_code=journal.code), end="...") for article in submission_models.Article.objects.filter(journal=journal): - stage_log_objects = submission_models.ArticleStageLog.objects.filter(article=article).order_by('date_time') + stage_log_objects = submission_models.ArticleStageLog.objects.filter( + article=article + ).order_by("date_time") if article.is_import: pass else: add_workflow_log_entries(article, stage_log_objects) - print('[OK]') + print("[OK]") def update_settings(): @@ -88,9 +106,9 @@ def delete_existing_workflows(): def execute(): - shared.check_version(script='1.3') + shared.check_version(script="1.3") # delete_existing_workflows() # run_journal_signals() # process_article_workflow() # update_settings() - shared.set_version('1.3') + shared.set_version("1.3") diff --git a/src/utils/upgrade/shared.py b/src/utils/upgrade/shared.py index 6122546c28..ab57fc7252 100644 --- a/src/utils/upgrade/shared.py +++ b/src/utils/upgrade/shared.py @@ -7,15 +7,14 @@ def versions(): - path = os.path.join(settings.BASE_DIR, 'utils', 'upgrade', 'versions.json') - with open(path, 'r', encoding="utf-8") as f: - + path = os.path.join(settings.BASE_DIR, "utils", "upgrade", "versions.json") + with open(path, "r", encoding="utf-8") as f: versions = json.loads(f.read()) return versions def current_version(): - versions = models.Version.objects.all().order_by('-date') + versions = models.Version.objects.all().order_by("-date") if versions: return versions[0] @@ -29,12 +28,14 @@ def set_version(version): obj, created = models.Version.objects.get_or_create(number=version) if not created: - print('WARNING: This version was already found so was not created.') + print("WARNING: This version was already found so was not created.") def check_version(script): version = current_version() if version and version.number == script: - input('This upgrade appears to have already been run. ' - 'Press [Enter] to continue running, press [Ctrl + C] to exit.') + input( + "This upgrade appears to have already been run. " + "Press [Enter] to continue running, press [Ctrl + C] to exit." + ) diff --git a/src/utils/urls.py b/src/utils/urls.py index 2e6af5b25d..787fbb9fa8 100644 --- a/src/utils/urls.py +++ b/src/utils/urls.py @@ -10,8 +10,6 @@ urlpatterns = [ - # Editor URLs - re_path(r'^mailgun_webhook', views.mailgun_webhook, name='mailgun_webhook'), - + re_path(r"^mailgun_webhook", views.mailgun_webhook, name="mailgun_webhook"), ] diff --git a/src/utils/workflow_tasks.py b/src/utils/workflow_tasks.py index a9edb32b19..5aca314eab 100755 --- a/src/utils/workflow_tasks.py +++ b/src/utils/workflow_tasks.py @@ -8,21 +8,21 @@ from events import logic as event_logic -ASSIGN_EDITORS_TITLE = 'New manuscript requires assignment' -SELECT_REVIEWERS_TITLE = 'Manuscript requires reviewers' -DO_REVIEW_TITLE = 'Review Request' -PERFORM_REVIEW_TITLE = 'Peer Review' +ASSIGN_EDITORS_TITLE = "New manuscript requires assignment" +SELECT_REVIEWERS_TITLE = "Manuscript requires reviewers" +DO_REVIEW_TITLE = "Review Request" +PERFORM_REVIEW_TITLE = "Peer Review" def assign_editors(**kwargs): - article = kwargs.get('article') + article = kwargs.get("article") task_dict = { - 'object': article, - 'title': ASSIGN_EDITORS_TITLE, - 'description': 'An article has been submitted. You must now assign an editor to handle it.', - 'link': reverse('review_unassigned_article', kwargs={'article_id': article.id}), - 'due': article.date_submitted + "object": article, + "title": ASSIGN_EDITORS_TITLE, + "description": "An article has been submitted. You must now assign an editor to handle it.", + "link": reverse("review_unassigned_article", kwargs={"article_id": article.id}), + "due": article.date_submitted, } task = models.Task.objects.create(**task_dict) @@ -31,25 +31,29 @@ def assign_editors(**kwargs): # when the article is assigned on_article_assigned, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_ASSIGNED) + event_name=event_logic.Events.ON_ARTICLE_ASSIGNED + ) task.complete_events.add(on_article_assigned.pk) # when the article is re-submitted (to avoid duplicate tasks) on_article_submitted, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_SUBMITTED) + event_name=event_logic.Events.ON_ARTICLE_SUBMITTED + ) task.complete_events.add(on_article_submitted.pk) # when the article is rejected on_article_declined, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_DECLINED) + event_name=event_logic.Events.ON_ARTICLE_DECLINED + ) task.complete_events.add(on_article_declined.pk) # when the article is accepted on_article_accepted, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_ACCEPTED) + event_name=event_logic.Events.ON_ARTICLE_ACCEPTED + ) task.complete_events.add(on_article_accepted.pk) - all_editors = models.AccountRole.objects.filter(role__slug='editor') + all_editors = models.AccountRole.objects.filter(role__slug="editor") for editor in all_editors: task.assignees.add(editor.user) @@ -59,15 +63,15 @@ def assign_editors(**kwargs): # TODO: we need a "move to review" task for when editors have been assigned def select_reviewers(**kwargs): - assignment = kwargs.get('editor_assignment') + assignment = kwargs.get("editor_assignment") article = assignment.article task_dict = { - 'object': article, - 'title': SELECT_REVIEWERS_TITLE, - 'description': 'You have been assigned as an article editor. Please initiate the review process.', - 'link': reverse('review_in_review', kwargs={'article_id': article.id}), - 'due': article.date_submitted + "object": article, + "title": SELECT_REVIEWERS_TITLE, + "description": "You have been assigned as an article editor. Please initiate the review process.", + "link": reverse("review_in_review", kwargs={"article_id": article.id}), + "due": article.date_submitted, } task = models.Task.objects.create(**task_dict) @@ -76,15 +80,17 @@ def select_reviewers(**kwargs): # when the article is rejected on_article_declined, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_DECLINED) + event_name=event_logic.Events.ON_ARTICLE_DECLINED + ) task.complete_events.add(on_article_declined.pk) # when the article is accepted on_article_accepted, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_ARTICLE_ACCEPTED) + event_name=event_logic.Events.ON_ARTICLE_ACCEPTED + ) task.complete_events.add(on_article_accepted.pk) - all_editors = models.AccountRole.objects.filter(role__slug='editor') + all_editors = models.AccountRole.objects.filter(role__slug="editor") for editor in all_editors: task.assignees.add(editor.user) @@ -93,15 +99,15 @@ def select_reviewers(**kwargs): def do_review_task(**kwargs): - review_assignment = kwargs.get('review_assignment') + review_assignment = kwargs.get("review_assignment") task_dict = { - 'object': review_assignment.article, - 'title': DO_REVIEW_TITLE, - 'description': 'You have been asked to complete a review of this article, please let the Editor know if you ' - 'are able to do so.', - 'link': reverse('review_requests'), - 'due': review_assignment.date_due + "object": review_assignment.article, + "title": DO_REVIEW_TITLE, + "description": "You have been asked to complete a review of this article, please let the Editor know if you " + "are able to do so.", + "link": reverse("review_requests"), + "due": review_assignment.date_due, } task = models.Task.objects.create(**task_dict) @@ -110,17 +116,20 @@ def do_review_task(**kwargs): # when the review is accepted on_accepted, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_REVIEWER_ACCEPTED) + event_name=event_logic.Events.ON_REVIEWER_ACCEPTED + ) task.complete_events.add(on_accepted.pk) # when the review is declined on_declined, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_REVIEWER_DECLINED) + event_name=event_logic.Events.ON_REVIEWER_DECLINED + ) task.complete_events.add(on_declined.pk) # when the review is closed on_review_closed, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_REVIEW_CLOSED) + event_name=event_logic.Events.ON_REVIEW_CLOSED + ) task.complete_events.add(on_review_closed.pk) task.assignees.add(review_assignment.reviewer) @@ -128,15 +137,15 @@ def do_review_task(**kwargs): def perform_review_task(**kwargs): - review_assignment = kwargs.get('review_assignment') + review_assignment = kwargs.get("review_assignment") task_dict = { - 'object': review_assignment.article, - 'title': PERFORM_REVIEW_TITLE, - 'description': 'You have been asked to complete a review of this article, please download the relevant files ' - 'and submit your decision to the editor.', - 'link': reverse('do_review', kwargs={'assignment_id': review_assignment.id}), - 'due': review_assignment.date_due + "object": review_assignment.article, + "title": PERFORM_REVIEW_TITLE, + "description": "You have been asked to complete a review of this article, please download the relevant files " + "and submit your decision to the editor.", + "link": reverse("do_review", kwargs={"assignment_id": review_assignment.id}), + "due": review_assignment.date_due, } task = models.Task.objects.create(**task_dict) @@ -145,12 +154,14 @@ def perform_review_task(**kwargs): # when the reviewer completes his or her review on_review_complete, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_REVIEW_COMPLETE) + event_name=event_logic.Events.ON_REVIEW_COMPLETE + ) task.complete_events.add(on_review_complete.pk) # when the review is closed on_review_closed, created = models.TaskCompleteEvents.objects.get_or_create( - event_name=event_logic.Events.ON_REVIEW_CLOSED) + event_name=event_logic.Events.ON_REVIEW_CLOSED + ) task.complete_events.add(on_review_closed.pk) task.assignees.add(review_assignment.reviewer) diff --git a/src/workflow/apps.py b/src/workflow/apps.py index 3a591ea872..03236dcf24 100644 --- a/src/workflow/apps.py +++ b/src/workflow/apps.py @@ -2,4 +2,4 @@ class WorkflowConfig(AppConfig): - name = 'workflow' + name = "workflow" diff --git a/src/workflow/logic.py b/src/workflow/logic.py index f72325a202..f0a639969c 100644 --- a/src/workflow/logic.py +++ b/src/workflow/logic.py @@ -76,7 +76,7 @@ def stages_in_between(from_stage, to_stage, article): stages = [log.element.stage for log in article.workflow_stages().reverse()] stage_index = stages.index(to_stage) - return stages[:stage_index+1] + return stages[: stage_index + 1] def move_to_stage(from_stage, to_stage, article): diff --git a/src/workflow/urls.py b/src/workflow/urls.py index d2ab80773b..835d241ddc 100644 --- a/src/workflow/urls.py +++ b/src/workflow/urls.py @@ -9,10 +9,14 @@ urlpatterns = [ - re_path(r'^article/(?P\d+)/$', + re_path( + r"^article/(?P\d+)/$", views.manage_article_workflow, - name='manage_article_workflow'), - re_path(r'^article/(?P\d+)/move_to_next/$', + name="manage_article_workflow", + ), + re_path( + r"^article/(?P\d+)/move_to_next/$", views.move_to_next_workflow_element, - name='move_to_next_workflow_element'), + name="move_to_next_workflow_element", + ), ] diff --git a/src/workflow/views.py b/src/workflow/views.py index 28e82608b4..3bd6d8a355 100644 --- a/src/workflow/views.py +++ b/src/workflow/views.py @@ -21,32 +21,30 @@ def manage_article_workflow(request, article_id): """ article = get_object_or_404( - submission_models.Article, - pk=article_id, - journal=request.journal + submission_models.Article, pk=article_id, journal=request.journal ) if request.POST: - if 'stage_to' in request.POST: - stage_to = request.POST.get('stage_to') + if "stage_to" in request.POST: + stage_to = request.POST.get("stage_to") stages_to_process = logic.move_to_stage( article.stage, stage_to, article, ) - stages_string = ', '.join(stages_to_process) + stages_string = ", ".join(stages_to_process) messages.add_message( request, messages.INFO, - 'Processing: {}'.format(stages_string), + "Processing: {}".format(stages_string), ) - elif 'archive' in request.POST: + elif "archive" in request.POST: utils_models.LogEntry.add_entry( - types='Workflow', - description='Article has been archived.', - level='Info', + types="Workflow", + description="Article has been archived.", + level="Info", actor=request.user, target=article, ) @@ -55,19 +53,16 @@ def manage_article_workflow(request, article_id): messages.add_message( request, messages.SUCCESS, - 'Article has been archived.', + "Article has been archived.", ) return redirect( - reverse( - 'manage_article_workflow', - kwargs={'article_id': article.pk} - ) + reverse("manage_article_workflow", kwargs={"article_id": article.pk}) ) - template = 'workflow/manage_article_workflow.html' + template = "workflow/manage_article_workflow.html" context = { - 'article': article, + "article": article, } return render(request, template, context) @@ -78,25 +73,23 @@ def manage_article_workflow(request, article_id): @editor_user_required def move_to_next_workflow_element(request, article_id): article = get_object_or_404( - submission_models.Article, - pk=article_id, - journal=request.journal + submission_models.Article, pk=article_id, journal=request.journal ) - current_stage = request.POST.get('current_stage', None) + current_stage = request.POST.get("current_stage", None) if current_stage != article.stage: messages.add_message( request, messages.ERROR, - 'Could not change stage or duplicate request.', + "Could not change stage or duplicate request.", ) return redirect(article.current_workflow_element_url) workflow_kwargs = { - 'handshake_url': article.current_workflow_element.handshake_url, - 'request': request, - 'article': article, - 'switch_stage': True + "handshake_url": article.current_workflow_element.handshake_url, + "request": request, + "article": article, + "switch_stage": True, } return event_logic.Events.raise_event(