浏览代码

Use ruff instead of black and flake8 (#643)

We switched the project templates to ruff in 3.0, and it has been
working out well. Time to switch the main project over to it.

* All configs are now in pyproject.toml.
* setup.cfg removed.
* Azure pipelines and contributing guide are both updated accordingly.
* Project has been formatted with ruff.
Vince Salvino 10 月之前
父节点
当前提交
526939e750
共有 41 个文件被更改,包括 412 次插入372 次删除
  1. 4 9
      azure-pipelines.yml
  2. 1 4
      ci/spellcheck.ps1
  3. 5 2
      coderedcms/admin_urls.py
  4. 2 2
      coderedcms/api/mailchimp.py
  5. 2 1
      coderedcms/bin/coderedcms.py
  6. 49 55
      coderedcms/blocks/__init__.py
  7. 1 1
      coderedcms/blocks/base_blocks.py
  8. 4 6
      coderedcms/blocks/content_blocks.py
  9. 8 9
      coderedcms/blocks/html_blocks.py
  10. 2 1
      coderedcms/blocks/layout_blocks.py
  11. 9 10
      coderedcms/blocks/stream_form_blocks.py
  12. 4 2
      coderedcms/forms.py
  13. 3 2
      coderedcms/importexport.py
  14. 11 11
      coderedcms/models/integration_models.py
  15. 68 60
      coderedcms/models/page_models.py
  16. 8 11
      coderedcms/models/snippet_models.py
  17. 6 7
      coderedcms/models/tests/test_navbars_and_footers.py
  18. 25 27
      coderedcms/models/tests/test_page_models.py
  19. 5 6
      coderedcms/models/tests/test_wagtailsettings_models.py
  20. 19 18
      coderedcms/models/wagtailsettings_models.py
  21. 2 0
      coderedcms/search_urls.py
  22. 1 0
      coderedcms/settings.py
  23. 8 5
      coderedcms/templatetags/coderedcms_tags.py
  24. 1 0
      coderedcms/tests/test_bin.py
  25. 2 1
      coderedcms/tests/test_templates.py
  26. 9 11
      coderedcms/tests/test_urls.py
  27. 13 14
      coderedcms/tests/testapp/models.py
  28. 5 1
      coderedcms/tests/urls.py
  29. 12 11
      coderedcms/urls.py
  30. 1 1
      coderedcms/utils.py
  31. 23 22
      coderedcms/views.py
  32. 12 14
      coderedcms/wagtail_flexible_forms/blocks.py
  33. 0 1
      coderedcms/wagtail_flexible_forms/edit_handlers.py
  34. 31 26
      coderedcms/wagtail_flexible_forms/models.py
  35. 4 3
      coderedcms/wagtail_hooks.py
  36. 4 5
      docs/contributing/index.rst
  37. 6 0
      docs/releases/v4.0.0.rst
  38. 37 1
      pyproject.toml
  39. 2 2
      requirements-ci.txt
  40. 0 10
      setup.cfg
  41. 3 0
      setup.py

+ 4 - 9
azure-pipelines.yml

@@ -1,4 +1,3 @@
-# Python Django
 # Test a Django project on multiple versions of Python.
 # Test a Django project on multiple versions of Python.
 # Add steps that analyze code, save build artifacts, deploy, and more:
 # Add steps that analyze code, save build artifacts, deploy, and more:
 # https://docs.microsoft.com/azure/devops/pipelines/languages/python
 # https://docs.microsoft.com/azure/devops/pipelines/languages/python
@@ -13,13 +12,10 @@
 #
 #
 # Use PowerShell Core for any utility scripts so they are re-usable across
 # Use PowerShell Core for any utility scripts so they are re-usable across
 # Windows, macOS, and Linux.
 # Windows, macOS, and Linux.
-#
-
 
 
 trigger:
 trigger:
   - main
   - main
 
 
-
 stages:
 stages:
 - stage: Unit_Tests
 - stage: Unit_Tests
   displayName: Unit Tests
   displayName: Unit Tests
@@ -71,7 +67,6 @@ stages:
 
 
     - script: |
     - script: |
         cd testproject/
         cd testproject/
-        touch requirements-dev.txt
         python -m pip install -r requirements-dev.txt
         python -m pip install -r requirements-dev.txt
         python manage.py makemigrations --check
         python manage.py makemigrations --check
       displayName: 'CR-QC: Check migrations'
       displayName: 'CR-QC: Check migrations'
@@ -117,11 +112,11 @@ stages:
     - pwsh: ./ci/spellcheck.ps1
     - pwsh: ./ci/spellcheck.ps1
       displayName: 'CR-QC: Spelling'
       displayName: 'CR-QC: Spelling'
 
 
-    - script: black --check .
+    - script: ruff format --check .
-      displayName: 'CR-QC: Black'
+      displayName: 'CR-QC: Ruff Format'
 
 
-    - script: flake8 .
+    - script: ruff check .
-      displayName: 'CR-QC: Flake8'
+      displayName: 'CR-QC: Ruff Check'
 
 
   - job: codecov
   - job: codecov
     displayName: Code Coverage
     displayName: Code Coverage

+ 1 - 4
ci/spellcheck.ps1

@@ -15,10 +15,7 @@ Push-Location $projectDir
 $ExitCode = 0
 $ExitCode = 0
 
 
 # Run spell checker.
 # Run spell checker.
-codespell `
+codespell coderedcms docs
-    --skip="migrations,vendor,_build,*.css.map,*.jpg,*.png,*.pyc" `
-    --ignore-words-list="assertIn" `
-    coderedcms docs
 $ExitCode = $LastExitCode
 $ExitCode = $LastExitCode
 
 
 # Print output.
 # Print output.

+ 5 - 2
coderedcms/admin_urls.py

@@ -1,6 +1,9 @@
-from django.urls import include, path
+from django.urls import include
+from django.urls import path
 from wagtail.admin import urls as wagtailadmin_urls
 from wagtail.admin import urls as wagtailadmin_urls
-from coderedcms.views import import_index, import_pages_from_csv_file
+
+from coderedcms.views import import_index
+from coderedcms.views import import_pages_from_csv_file
 
 
 
 
 urlpatterns = [
 urlpatterns = [

+ 2 - 2
coderedcms/api/mailchimp.py

@@ -1,7 +1,7 @@
+import requests
 from wagtail.models import Site
 from wagtail.models import Site
-from coderedcms.models.wagtailsettings_models import LayoutSettings
 
 
-import requests
+from coderedcms.models.wagtailsettings_models import LayoutSettings
 
 
 
 
 class MailchimpApi:
 class MailchimpApi:

+ 2 - 1
coderedcms/bin/coderedcms.py

@@ -46,9 +46,10 @@ class CreateProject(TemplateCommand):
         options["secret_key"] = get_random_secret_key()
         options["secret_key"] = get_random_secret_key()
 
 
         # Handle custom template logic
         # Handle custom template logic
-        import coderedcms
         import wagtail
         import wagtail
 
 
+        import coderedcms
+
         crx_path = os.path.dirname(coderedcms.__file__)
         crx_path = os.path.dirname(coderedcms.__file__)
         if not options["template"]:
         if not options["template"]:
             options["template"] = "basic"
             options["template"] = "basic"

+ 49 - 55
coderedcms/blocks/__init__.py

@@ -5,63 +5,57 @@ single `blocks` module.
 """
 """
 
 
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-
 from wagtail import blocks
 from wagtail import blocks
 
 
-from .stream_form_blocks import (
+from .base_blocks import BaseBlock  # noqa
-    CoderedStreamFormCharFieldBlock,
+from .base_blocks import BaseLayoutBlock  # noqa
-    CoderedStreamFormCheckboxesFieldBlock,
+from .base_blocks import BaseLinkBlock  # noqa
-    CoderedStreamFormCheckboxFieldBlock,
+from .base_blocks import ClassifierTermChooserBlock  # noqa
-    CoderedStreamFormDateFieldBlock,
+from .base_blocks import CoderedAdvColumnSettings  # noqa
-    CoderedStreamFormDateTimeFieldBlock,
+from .base_blocks import CoderedAdvSettings  # noqa
-    CoderedStreamFormDropdownFieldBlock,
+from .base_blocks import CoderedAdvTrackingSettings  # noqa
-    CoderedStreamFormFileFieldBlock,
+from .base_blocks import CollectionChooserBlock  # noqa
-    CoderedStreamFormImageFieldBlock,
+from .base_blocks import LinkStructValue  # noqa
-    CoderedStreamFormNumberFieldBlock,
+from .content_blocks import AccordionBlock
-    CoderedStreamFormRadioButtonsFieldBlock,
+from .content_blocks import CardBlock
-    CoderedStreamFormStepBlock,
+from .content_blocks import CarouselBlock
-    CoderedStreamFormTextFieldBlock,
+from .content_blocks import ContentWallBlock  # noqa
-    CoderedStreamFormTimeFieldBlock,
+from .content_blocks import FilmStripBlock
-)
+from .content_blocks import ImageGalleryBlock
-from .html_blocks import (
+from .content_blocks import ModalBlock
-    ButtonBlock,
+from .content_blocks import NavDocumentLinkWithSubLinkBlock
-    EmbedGoogleMapBlock,
+from .content_blocks import NavExternalLinkWithSubLinkBlock
-    ImageBlock,
+from .content_blocks import NavPageLinkWithSubLinkBlock
-    ImageLinkBlock,
+from .content_blocks import PriceListBlock
-    DownloadBlock,
+from .content_blocks import ReusableContentBlock
-    EmbedVideoBlock,
+from .html_blocks import ButtonBlock
-    PageListBlock,
+from .html_blocks import DownloadBlock
-    PagePreviewBlock,
+from .html_blocks import EmbedGoogleMapBlock
-    QuoteBlock,
+from .html_blocks import EmbedVideoBlock
-    RichTextBlock,
+from .html_blocks import ImageBlock
-    TableBlock,
+from .html_blocks import ImageLinkBlock
-)
+from .html_blocks import PageListBlock
-from .content_blocks import (  # noqa
+from .html_blocks import PagePreviewBlock
-    AccordionBlock,
+from .html_blocks import QuoteBlock
-    CardBlock,
+from .html_blocks import RichTextBlock
-    CarouselBlock,
+from .html_blocks import TableBlock
-    ContentWallBlock,
+from .layout_blocks import CardGridBlock
-    FilmStripBlock,
+from .layout_blocks import GridBlock
-    ImageGalleryBlock,
+from .layout_blocks import HeroBlock
-    ModalBlock,
+from .stream_form_blocks import CoderedStreamFormCharFieldBlock
-    NavDocumentLinkWithSubLinkBlock,
+from .stream_form_blocks import CoderedStreamFormCheckboxesFieldBlock
-    NavExternalLinkWithSubLinkBlock,
+from .stream_form_blocks import CoderedStreamFormCheckboxFieldBlock
-    NavPageLinkWithSubLinkBlock,
+from .stream_form_blocks import CoderedStreamFormDateFieldBlock
-    PriceListBlock,
+from .stream_form_blocks import CoderedStreamFormDateTimeFieldBlock
-    ReusableContentBlock,
+from .stream_form_blocks import CoderedStreamFormDropdownFieldBlock
-)
+from .stream_form_blocks import CoderedStreamFormFileFieldBlock
-from .layout_blocks import CardGridBlock, GridBlock, HeroBlock
+from .stream_form_blocks import CoderedStreamFormImageFieldBlock
-from .base_blocks import (  # noqa
+from .stream_form_blocks import CoderedStreamFormNumberFieldBlock
-    BaseBlock,
+from .stream_form_blocks import CoderedStreamFormRadioButtonsFieldBlock
-    BaseLayoutBlock,
+from .stream_form_blocks import CoderedStreamFormStepBlock
-    BaseLinkBlock,
+from .stream_form_blocks import CoderedStreamFormTextFieldBlock
-    ClassifierTermChooserBlock,
+from .stream_form_blocks import CoderedStreamFormTimeFieldBlock
-    CoderedAdvColumnSettings,
+
-    CoderedAdvSettings,
-    CoderedAdvTrackingSettings,
-    CollectionChooserBlock,
-    LinkStructValue,
-)
 
 
 # Collections of blocks commonly used together.
 # Collections of blocks commonly used together.
 
 

+ 1 - 1
coderedcms/blocks/base_blocks.py

@@ -8,9 +8,9 @@ from django.utils.functional import cached_property
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from wagtail import blocks
 from wagtail import blocks
-from wagtail.models import Collection
 from wagtail.coreutils import resolve_model_string
 from wagtail.coreutils import resolve_model_string
 from wagtail.documents.blocks import DocumentChooserBlock
 from wagtail.documents.blocks import DocumentChooserBlock
+from wagtail.models import Collection
 
 
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
 
 

+ 4 - 6
coderedcms/blocks/content_blocks.py

@@ -9,12 +9,10 @@ from wagtail.documents.blocks import DocumentChooserBlock
 from wagtail.images.blocks import ImageChooserBlock
 from wagtail.images.blocks import ImageChooserBlock
 from wagtail.snippets.blocks import SnippetChooserBlock
 from wagtail.snippets.blocks import SnippetChooserBlock
 
 
-from .base_blocks import (
+from .base_blocks import BaseBlock
-    BaseBlock,
+from .base_blocks import BaseLayoutBlock
-    BaseLayoutBlock,
+from .base_blocks import ButtonMixin
-    ButtonMixin,
+from .base_blocks import CollectionChooserBlock
-    CollectionChooserBlock,
-)
 from .html_blocks import ButtonBlock
 from .html_blocks import ButtonBlock
 
 
 
 

+ 8 - 9
coderedcms/blocks/html_blocks.py

@@ -8,21 +8,20 @@ creating recursion.
 """
 """
 
 
 import logging
 import logging
+
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-from wagtail.contrib.table_block.blocks import TableBlock as WagtailTableBlock
 from wagtail import blocks
 from wagtail import blocks
+from wagtail.contrib.table_block.blocks import TableBlock as WagtailTableBlock
 from wagtail.documents.blocks import DocumentChooserBlock
 from wagtail.documents.blocks import DocumentChooserBlock
 from wagtail.embeds.blocks import EmbedBlock
 from wagtail.embeds.blocks import EmbedBlock
 from wagtail.images.blocks import ImageChooserBlock
 from wagtail.images.blocks import ImageChooserBlock
 
 
-from .base_blocks import (
+from .base_blocks import BaseBlock
-    BaseBlock,
+from .base_blocks import BaseLinkBlock
-    BaseLinkBlock,
+from .base_blocks import ButtonMixin
-    ButtonMixin,
+from .base_blocks import ClassifierTermChooserBlock
-    ClassifierTermChooserBlock,
+from .base_blocks import CoderedAdvTrackingSettings
-    CoderedAdvTrackingSettings,
+from .base_blocks import LinkStructValue
-    LinkStructValue,
-)
 
 
 
 
 logger = logging.getLogger("coderedcms")
 logger = logging.getLogger("coderedcms")

+ 2 - 1
coderedcms/blocks/layout_blocks.py

@@ -9,7 +9,8 @@ from wagtail.images.blocks import ImageChooserBlock
 
 
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
 
 
-from .base_blocks import BaseLayoutBlock, CoderedAdvColumnSettings
+from .base_blocks import BaseLayoutBlock
+from .base_blocks import CoderedAdvColumnSettings
 
 
 
 
 # Level 1 layout blocks
 # Level 1 layout blocks

+ 9 - 10
coderedcms/blocks/stream_form_blocks.py

@@ -1,17 +1,16 @@
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from wagtail import blocks
 from wagtail import blocks
 
 
+from coderedcms.blocks.base_blocks import BaseBlock
+from coderedcms.blocks.base_blocks import CoderedAdvSettings
+from coderedcms.forms import CoderedDateField
+from coderedcms.forms import CoderedDateInput
+from coderedcms.forms import CoderedDateTimeField
+from coderedcms.forms import CoderedDateTimeInput
+from coderedcms.forms import CoderedTimeField
+from coderedcms.forms import CoderedTimeInput
+from coderedcms.forms import SecureFileField
 from coderedcms.wagtail_flexible_forms import blocks as form_blocks
 from coderedcms.wagtail_flexible_forms import blocks as form_blocks
-from coderedcms.blocks.base_blocks import BaseBlock, CoderedAdvSettings
-from coderedcms.forms import (
-    CoderedDateField,
-    CoderedDateInput,
-    CoderedDateTimeField,
-    CoderedDateTimeInput,
-    CoderedTimeField,
-    CoderedTimeInput,
-    SecureFileField,
-)
 
 
 
 
 class CoderedFormAdvSettings(CoderedAdvSettings):
 class CoderedFormAdvSettings(CoderedAdvSettings):

+ 4 - 2
coderedcms/forms.py

@@ -4,21 +4,23 @@ Enhancements to wagtail.contrib.forms.
 
 
 import csv
 import csv
 import os
 import os
+
 from django import forms
 from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.db import models
 from django.http import HttpResponse
 from django.http import HttpResponse
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
+from wagtail.contrib.forms.forms import FormBuilder
+from wagtail.contrib.forms.models import AbstractFormField
 from wagtail.contrib.forms.views import (
 from wagtail.contrib.forms.views import (
     SubmissionsListView as WagtailSubmissionsListView,
     SubmissionsListView as WagtailSubmissionsListView,
 )
 )
-from wagtail.contrib.forms.forms import FormBuilder
-from wagtail.contrib.forms.models import AbstractFormField
 
 
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
 from coderedcms.utils import attempt_protected_media_value_conversion
 from coderedcms.utils import attempt_protected_media_value_conversion
 
 
+
 FORM_FIELD_CHOICES = (
 FORM_FIELD_CHOICES = (
     (
     (
         _("Text"),
         _("Text"),

+ 3 - 2
coderedcms/importexport.py

@@ -8,13 +8,14 @@ or simply deprecate all of this functionality.
 See: https://github.com/torchbox/wagtail-import-export/
 See: https://github.com/torchbox/wagtail-import-export/
 """
 """
 
 
-import csv
 import copy
 import copy
+import csv
 
 
 from django import forms
 from django import forms
 from django.apps import apps
 from django.apps import apps
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
-from django.db import models, transaction
+from django.db import models
+from django.db import transaction
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 from modelcluster.models import get_all_child_relations
 from modelcluster.models import get_all_child_relations
 from wagtail.admin.widgets import AdminPageChooser
 from wagtail.admin.widgets import AdminPageChooser

+ 11 - 11
coderedcms/models/integration_models.py

@@ -1,16 +1,16 @@
+import json
+
 from django.db import models
 from django.db import models
 from django.forms.widgets import Input
 from django.forms.widgets import Input
-from django.template import Context, Template
+from django.template import Context
+from django.template import Template
 from django.template.loader import render_to_string
 from django.template.loader import render_to_string
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-
-from wagtail.admin.panels import FieldPanel
 from wagtail import hooks
 from wagtail import hooks
+from wagtail.admin.panels import FieldPanel
 
 
 from coderedcms.api.mailchimp import MailchimpApi
 from coderedcms.api.mailchimp import MailchimpApi
 
 
-import json
-
 
 
 class MailchimpSubscriberIntegrationWidget(Input):
 class MailchimpSubscriberIntegrationWidget(Input):
     template_name = (
     template_name = (
@@ -95,14 +95,14 @@ class MailchimpSubscriberIntegrationWidget(Input):
                 }
                 }
 
 
                 list_library[mlist["id"]]["merge_fields"] = (
                 list_library[mlist["id"]]["merge_fields"] = (
-                    mailchimp.get_merge_fields_for_list(mlist["id"])[
+                    mailchimp.get_merge_fields_for_list(
-                        "merge_fields"
+                        mlist["id"]
-                    ]
+                    )["merge_fields"]
                 )
                 )
                 list_library[mlist["id"]]["interest_categories"] = (
                 list_library[mlist["id"]]["interest_categories"] = (
-                    mailchimp.get_interest_categories_for_list(mlist["id"])[
+                    mailchimp.get_interest_categories_for_list(
-                        "categories"
+                        mlist["id"]
-                    ]
+                    )["categories"]
                 )
                 )
 
 
                 for category in list_library[mlist["id"]][
                 for category in list_library[mlist["id"]][

+ 68 - 60
coderedcms/models/page_models.py

@@ -6,94 +6,102 @@ import json
 import logging
 import logging
 import os
 import os
 import warnings
 import warnings
-from datetime import date, datetime
+from datetime import date
-from typing import Dict, List, Optional, TYPE_CHECKING, Union, Tuple
+from datetime import datetime
+from pathlib import Path
+from typing import TYPE_CHECKING
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Tuple
+from typing import Union
+
+import geocoder
 
 
 # This is a requirement for icalendar, even if django doesn't require it
 # This is a requirement for icalendar, even if django doesn't require it
 import pytz
 import pytz
-
-import geocoder
 from django import forms
 from django import forms
 from django.conf import settings
 from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
-from django.core.files.uploadedfile import (
-    InMemoryUploadedFile,
-    TemporaryUploadedFile,
-)
 from django.core.files.storage import FileSystemStorage
 from django.core.files.storage import FileSystemStorage
+from django.core.files.uploadedfile import InMemoryUploadedFile
+from django.core.files.uploadedfile import TemporaryUploadedFile
 from django.core.mail import EmailMessage
 from django.core.mail import EmailMessage
-from django.core.paginator import (
+from django.core.paginator import EmptyPage
-    Paginator,
+from django.core.paginator import InvalidPage
-    InvalidPage,
+from django.core.paginator import PageNotAnInteger
-    EmptyPage,
+from django.core.paginator import Paginator
-    PageNotAnInteger,
-)
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.serializers.json import DjangoJSONEncoder
-from django.core.validators import MaxValueValidator, MinValueValidator
+from django.core.validators import MaxValueValidator
+from django.core.validators import MinValueValidator
 from django.db import models
 from django.db import models
-from django.db.models.signals import post_delete, post_save
+from django.db.models.signals import post_delete
+from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.dispatch import receiver
-from django.http import JsonResponse, HttpResponseRedirect
+from django.http import HttpResponseRedirect
-from django.shortcuts import render, redirect
+from django.http import JsonResponse
-from django.template import Context, Template
+from django.shortcuts import redirect
+from django.shortcuts import render
+from django.template import Context
+from django.template import Template
 from django.template.loader import render_to_string
 from django.template.loader import render_to_string
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.html import strip_tags
 from django.utils.html import strip_tags
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-from eventtools.models import BaseEvent, BaseOccurrence
+from eventtools.models import BaseEvent
+from eventtools.models import BaseOccurrence
 from icalendar import Alarm
 from icalendar import Alarm
 from icalendar import Event as ICalEvent
 from icalendar import Event as ICalEvent
-from modelcluster.fields import ParentalKey, ParentalManyToManyField
 from modelcluster.contrib.taggit import ClusterTaggableManager
 from modelcluster.contrib.taggit import ClusterTaggableManager
-from pathlib import Path
+from modelcluster.fields import ParentalKey
+from modelcluster.fields import ParentalManyToManyField
 from taggit.models import TaggedItemBase
 from taggit.models import TaggedItemBase
-from wagtail.admin.panels import (
-    FieldPanel,
-    FieldRowPanel,
-    InlinePanel,
-    MultiFieldPanel,
-    ObjectList,
-    TabbedInterface,
-)
 from wagtail import hooks
 from wagtail import hooks
-from wagtail.fields import StreamField
+from wagtail.admin.panels import FieldPanel
-from wagtail.models import Orderable, PageBase, Page, Site
+from wagtail.admin.panels import FieldRowPanel
-from wagtail.coreutils import resolve_model_string
+from wagtail.admin.panels import InlinePanel
-from wagtail.contrib.forms.panels import FormSubmissionsPanel
+from wagtail.admin.panels import MultiFieldPanel
+from wagtail.admin.panels import ObjectList
+from wagtail.admin.panels import TabbedInterface
 from wagtail.contrib.forms.forms import WagtailAdminFormPageForm
 from wagtail.contrib.forms.forms import WagtailAdminFormPageForm
-from wagtail.images import get_image_model_string
 from wagtail.contrib.forms.models import FormSubmission
 from wagtail.contrib.forms.models import FormSubmission
+from wagtail.contrib.forms.panels import FormSubmissionsPanel
+from wagtail.coreutils import resolve_model_string
+from wagtail.fields import StreamField
+from wagtail.images import get_image_model_string
+from wagtail.models import Orderable
+from wagtail.models import Page
+from wagtail.models import PageBase
+from wagtail.models import Site
 from wagtail.search import index
 from wagtail.search import index
 from wagtail.utils.decorators import cached_classmethod
 from wagtail.utils.decorators import cached_classmethod
 from wagtailcache.cache import WagtailCacheMixin
 from wagtailcache.cache import WagtailCacheMixin
-from wagtailseo.models import SeoMixin, TwitterCard
+from wagtailseo.models import SeoMixin
-from wagtailseo.utils import get_struct_data_images, StructDataEncoder
+from wagtailseo.models import TwitterCard
+from wagtailseo.utils import StructDataEncoder
+from wagtailseo.utils import get_struct_data_images
 
 
 from coderedcms import utils
 from coderedcms import utils
-from coderedcms.blocks import (
+from coderedcms.blocks import CONTENT_STREAMBLOCKS
-    CONTENT_STREAMBLOCKS,
+from coderedcms.blocks import LAYOUT_STREAMBLOCKS
-    LAYOUT_STREAMBLOCKS,
+from coderedcms.blocks import STREAMFORM_BLOCKS
-    STREAMFORM_BLOCKS,
+from coderedcms.blocks import ContentWallBlock
-    ContentWallBlock,
+from coderedcms.fields import CoderedStreamField
-)
+from coderedcms.fields import ColorField
-from coderedcms.fields import CoderedStreamField, ColorField
+from coderedcms.forms import CoderedFormBuilder
-from coderedcms.forms import CoderedFormBuilder, CoderedSubmissionsListView
+from coderedcms.forms import CoderedSubmissionsListView
 from coderedcms.models.snippet_models import ClassifierTerm
 from coderedcms.models.snippet_models import ClassifierTerm
 from coderedcms.models.wagtailsettings_models import LayoutSettings
 from coderedcms.models.wagtailsettings_models import LayoutSettings
-from coderedcms.wagtail_flexible_forms.blocks import (
-    FormFieldBlock,
-    FormStepBlock,
-)
-from coderedcms.wagtail_flexible_forms.models import (
-    Step,
-    Steps,
-    StreamFormMixin,
-    StreamFormJSONEncoder,
-    SessionFormSubmission,
-    SubmissionRevision,
-)
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
+from coderedcms.wagtail_flexible_forms.blocks import FormFieldBlock
+from coderedcms.wagtail_flexible_forms.blocks import FormStepBlock
+from coderedcms.wagtail_flexible_forms.models import SessionFormSubmission
+from coderedcms.wagtail_flexible_forms.models import Step
+from coderedcms.wagtail_flexible_forms.models import Steps
+from coderedcms.wagtail_flexible_forms.models import StreamFormJSONEncoder
+from coderedcms.wagtail_flexible_forms.models import StreamFormMixin
+from coderedcms.wagtail_flexible_forms.models import SubmissionRevision
 from coderedcms.widgets import ClassifierSelectWidget
 from coderedcms.widgets import ClassifierSelectWidget
 
 
 
 
@@ -381,9 +389,9 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
             "*", []
             "*", []
         ) + crx_settings.CRX_FRONTEND_TEMPLATES_PAGES.get(klassname, [])
         ) + crx_settings.CRX_FRONTEND_TEMPLATES_PAGES.get(klassname, [])
 
 
-        self._meta.get_field("index_order_by").choices = (
+        self._meta.get_field(
-            self.index_order_by_choices
+            "index_order_by"
-        )
+        ).choices = self.index_order_by_choices
         self._meta.get_field("custom_template").choices = template_choices
         self._meta.get_field("custom_template").choices = template_choices
         if not self.id:
         if not self.id:
             self.index_order_by = self.index_order_by_default
             self.index_order_by = self.index_order_by_default

+ 8 - 11
coderedcms/models/snippet_models.py

@@ -7,19 +7,16 @@ from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from modelcluster.fields import ParentalKey
 from modelcluster.fields import ParentalKey
 from modelcluster.models import ClusterableModel
 from modelcluster.models import ClusterableModel
-from wagtail.admin.panels import (
+from wagtail.admin.panels import FieldPanel
-    FieldPanel,
+from wagtail.admin.panels import InlinePanel
-    InlinePanel,
+from wagtail.admin.panels import MultiFieldPanel
-    MultiFieldPanel,
+from wagtail.images import get_image_model_string
-)
 from wagtail.models import Orderable
 from wagtail.models import Orderable
 from wagtail.snippets.models import register_snippet
 from wagtail.snippets.models import register_snippet
-from wagtail.images import get_image_model_string
+
-from coderedcms.blocks import (
+from coderedcms.blocks import HTML_STREAMBLOCKS
-    HTML_STREAMBLOCKS,
+from coderedcms.blocks import LAYOUT_STREAMBLOCKS
-    LAYOUT_STREAMBLOCKS,
+from coderedcms.blocks import NAVIGATION_STREAMBLOCKS
-    NAVIGATION_STREAMBLOCKS,
-)
 from coderedcms.fields import CoderedStreamField
 from coderedcms.fields import CoderedStreamField
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
 
 

+ 6 - 7
coderedcms/models/tests/test_navbars_and_footers.py

@@ -1,14 +1,13 @@
 from django.test import Client
 from django.test import Client
-from wagtail.test.utils import WagtailPageTests
 from wagtail.models import Site
 from wagtail.models import Site
+from wagtail.test.utils import WagtailPageTests
 
 
+from coderedcms.models.snippet_models import Footer
+from coderedcms.models.snippet_models import Navbar
+from coderedcms.models.wagtailsettings_models import FooterOrderable
+from coderedcms.models.wagtailsettings_models import LayoutSettings
+from coderedcms.models.wagtailsettings_models import NavbarOrderable
 from coderedcms.tests.testapp.models import WebPage
 from coderedcms.tests.testapp.models import WebPage
-from coderedcms.models.snippet_models import Footer, Navbar
-from coderedcms.models.wagtailsettings_models import (
-    LayoutSettings,
-    NavbarOrderable,
-    FooterOrderable,
-)
 
 
 
 
 class NavbarFooterTestCase(WagtailPageTests):
 class NavbarFooterTestCase(WagtailPageTests):

+ 25 - 27
coderedcms/models/tests/test_page_models.py

@@ -1,35 +1,33 @@
 from datetime import timedelta
 from datetime import timedelta
+
 from django.test import Client
 from django.test import Client
 from django.utils import timezone
 from django.utils import timezone
 from wagtail.test.utils import WagtailPageTests
 from wagtail.test.utils import WagtailPageTests
 
 
-from coderedcms.models.page_models import (
+from coderedcms.models.page_models import CoderedArticleIndexPage
-    CoderedArticleIndexPage,
+from coderedcms.models.page_models import CoderedArticlePage
-    CoderedArticlePage,
+from coderedcms.models.page_models import CoderedEventIndexPage
-    CoderedEventIndexPage,
+from coderedcms.models.page_models import CoderedEventPage
-    CoderedEventPage,
+from coderedcms.models.page_models import CoderedFormPage
-    CoderedFormPage,
+from coderedcms.models.page_models import CoderedLocationIndexPage
-    CoderedLocationIndexPage,
+from coderedcms.models.page_models import CoderedLocationPage
-    CoderedLocationPage,
+from coderedcms.models.page_models import CoderedPage
-    CoderedPage,
+from coderedcms.models.page_models import CoderedStreamFormPage
-    CoderedStreamFormPage,
+from coderedcms.models.page_models import CoderedWebPage
-    CoderedWebPage,
+from coderedcms.models.page_models import get_page_models
-    get_page_models,
+from coderedcms.models.snippet_models import Classifier
-)
+from coderedcms.models.snippet_models import ClassifierTerm
-from coderedcms.models.snippet_models import Classifier, ClassifierTerm
+from coderedcms.tests.testapp.models import ArticleIndexPage
-from coderedcms.tests.testapp.models import (
+from coderedcms.tests.testapp.models import ArticlePage
-    ArticleIndexPage,
+from coderedcms.tests.testapp.models import EventIndexPage
-    ArticlePage,
+from coderedcms.tests.testapp.models import EventOccurrence
-    EventIndexPage,
+from coderedcms.tests.testapp.models import EventPage
-    EventPage,
+from coderedcms.tests.testapp.models import FormPage
-    EventOccurrence,
+from coderedcms.tests.testapp.models import IndexTestPage
-    FormPage,
+from coderedcms.tests.testapp.models import LocationIndexPage
-    IndexTestPage,
+from coderedcms.tests.testapp.models import LocationPage
-    LocationIndexPage,
+from coderedcms.tests.testapp.models import StreamFormPage
-    LocationPage,
+from coderedcms.tests.testapp.models import WebPage
-    StreamFormPage,
-    WebPage,
-)
 
 
 
 
 class BasicPageTestCase:
 class BasicPageTestCase:

+ 5 - 6
coderedcms/models/tests/test_wagtailsettings_models.py

@@ -1,14 +1,13 @@
 from unittest.mock import patch
 from unittest.mock import patch
 
 
-from django.test import Client, override_settings
+from django.test import Client
-from wagtail.test.utils import WagtailPageTests
+from django.test import override_settings
 from wagtail.models import Site
 from wagtail.models import Site
+from wagtail.test.utils import WagtailPageTests
 
 
+from coderedcms.models.wagtailsettings_models import AnalyticsSettings
+from coderedcms.models.wagtailsettings_models import maybe_register_setting
 from coderedcms.tests.testapp.models import WebPage
 from coderedcms.tests.testapp.models import WebPage
-from coderedcms.models.wagtailsettings_models import (
-    AnalyticsSettings,
-    maybe_register_setting,
-)
 
 
 
 
 class AnalyticsSettingsTestCase(WagtailPageTests):
 class AnalyticsSettingsTestCase(WagtailPageTests):

+ 19 - 18
coderedcms/models/wagtailsettings_models.py

@@ -8,18 +8,19 @@ from django.db import models
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from modelcluster.fields import ParentalKey
 from modelcluster.fields import ParentalKey
 from modelcluster.models import ClusterableModel
 from modelcluster.models import ClusterableModel
-from wagtail.admin.panels import (
+from wagtail.admin.panels import FieldPanel
-    FieldPanel,
+from wagtail.admin.panels import HelpPanel
-    InlinePanel,
+from wagtail.admin.panels import InlinePanel
-    HelpPanel,
+from wagtail.admin.panels import MultiFieldPanel
-    MultiFieldPanel,
+from wagtail.contrib.settings.models import BaseSiteSetting
-)
+from wagtail.contrib.settings.models import register_setting
-from wagtail.models import Orderable
-from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
 from wagtail.images import get_image_model_string
 from wagtail.images import get_image_model_string
+from wagtail.models import Orderable
+
 from coderedcms.fields import MonospaceField
 from coderedcms.fields import MonospaceField
+from coderedcms.models.snippet_models import Footer
+from coderedcms.models.snippet_models import Navbar
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
-from coderedcms.models.snippet_models import Navbar, Footer
 
 
 
 
 def maybe_register_setting(disable: bool, **kwargs):
 def maybe_register_setting(disable: bool, **kwargs):
@@ -208,15 +209,15 @@ class LayoutSettings(ClusterableModel, BaseSiteSetting):
         """
         """
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
         # Set choices dynamically.
         # Set choices dynamically.
-        self._meta.get_field("navbar_collapse_mode").choices = (
+        self._meta.get_field(
-            crx_settings.CRX_FRONTEND_NAVBAR_COLLAPSE_MODE_CHOICES
+            "navbar_collapse_mode"
-        )
+        ).choices = crx_settings.CRX_FRONTEND_NAVBAR_COLLAPSE_MODE_CHOICES
-        self._meta.get_field("navbar_color_scheme").choices = (
+        self._meta.get_field(
-            crx_settings.CRX_FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES
+            "navbar_color_scheme"
-        )
+        ).choices = crx_settings.CRX_FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES
-        self._meta.get_field("navbar_format").choices = (
+        self._meta.get_field(
-            crx_settings.CRX_FRONTEND_NAVBAR_FORMAT_CHOICES
+            "navbar_format"
-        )
+        ).choices = crx_settings.CRX_FRONTEND_NAVBAR_FORMAT_CHOICES
         # Set default dynamically.
         # Set default dynamically.
         if not self.id:
         if not self.id:
             self.navbar_class = crx_settings.CRX_FRONTEND_NAVBAR_CLASS_DEFAULT
             self.navbar_class = crx_settings.CRX_FRONTEND_NAVBAR_CLASS_DEFAULT

+ 2 - 0
coderedcms/search_urls.py

@@ -1,6 +1,8 @@
 from django.urls import path
 from django.urls import path
+
 from coderedcms.views import search
 from coderedcms.views import search
 
 
+
 urlpatterns = [
 urlpatterns = [
     path("", search, name="crx_search"),
     path("", search, name="crx_search"),
 ]
 ]

+ 1 - 0
coderedcms/settings.py

@@ -1,4 +1,5 @@
 import os
 import os
+
 from django.apps import apps
 from django.apps import apps
 from django.conf import settings
 from django.conf import settings
 
 

+ 8 - 5
coderedcms/templatetags/coderedcms_tags.py

@@ -1,21 +1,24 @@
-import string
 import random
 import random
+import string
 
 
 from bs4 import BeautifulSoup
 from bs4 import BeautifulSoup
 from django import template
 from django import template
 from django.db.models.query import QuerySet
 from django.db.models.query import QuerySet
 from django.forms import ClearableFileInput
 from django.forms import ClearableFileInput
 from django.utils.html import mark_safe
 from django.utils.html import mark_safe
-from wagtail.models import Collection
 from wagtail.images import get_image_model
 from wagtail.images import get_image_model
+from wagtail.models import Collection
 
 
-from coderedcms import utils, __version__
+from coderedcms import __version__
+from coderedcms import utils
 from coderedcms.blocks import CoderedAdvSettings
 from coderedcms.blocks import CoderedAdvSettings
 from coderedcms.forms import SearchForm
 from coderedcms.forms import SearchForm
-from coderedcms.models.snippet_models import Navbar, Footer
+from coderedcms.models.snippet_models import Footer
+from coderedcms.models.snippet_models import Navbar
+from coderedcms.models.wagtailsettings_models import LayoutSettings
 from coderedcms.settings import crx_settings as crx_settings_obj
 from coderedcms.settings import crx_settings as crx_settings_obj
 from coderedcms.settings import get_bootstrap_setting
 from coderedcms.settings import get_bootstrap_setting
-from coderedcms.models.wagtailsettings_models import LayoutSettings
+
 
 
 register = template.Library()
 register = template.Library()
 
 

+ 1 - 0
coderedcms/tests/test_bin.py

@@ -2,6 +2,7 @@ import os
 import shutil
 import shutil
 import sys
 import sys
 import unittest
 import unittest
+
 from coderedcms.bin.coderedcms import main as coderedcms_main
 from coderedcms.bin.coderedcms import main as coderedcms_main
 
 
 
 

+ 2 - 1
coderedcms/tests/test_templates.py

@@ -1,6 +1,7 @@
 import pytest
 import pytest
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
-from django.test import override_settings, TestCase
+from django.test import TestCase
+from django.test import override_settings
 
 
 
 
 EXPECTED_BANNER_HTML = """
 EXPECTED_BANNER_HTML = """

+ 9 - 11
coderedcms/tests/test_urls.py

@@ -1,23 +1,21 @@
-import pytest
 import unittest
 import unittest
-
 from ast import literal_eval
 from ast import literal_eval
 from datetime import timedelta
 from datetime import timedelta
 
 
-from django.urls import reverse
+import pytest
 from django.test import Client
 from django.test import Client
 from django.test.utils import override_settings
 from django.test.utils import override_settings
+from django.urls import reverse
 from django.utils import timezone
 from django.utils import timezone
-
+from wagtail.images.tests.utils import Image
-from wagtail.models import Site, Page
+from wagtail.images.tests.utils import get_test_image_file
-from wagtail.images.tests.utils import Image, get_test_image_file
+from wagtail.models import Page
+from wagtail.models import Site
 
 
 from coderedcms.models import LayoutSettings
 from coderedcms.models import LayoutSettings
-from coderedcms.tests.testapp.models import (
+from coderedcms.tests.testapp.models import EventIndexPage
-    EventPage,
+from coderedcms.tests.testapp.models import EventOccurrence
-    EventIndexPage,
+from coderedcms.tests.testapp.models import EventPage
-    EventOccurrence,
-)
 
 
 
 
 @pytest.mark.django_db
 @pytest.mark.django_db

+ 13 - 14
coderedcms/tests/testapp/models.py

@@ -1,19 +1,18 @@
-from coderedcms.models.page_models import CoderedPage
 from modelcluster.fields import ParentalKey
 from modelcluster.fields import ParentalKey
+
 from coderedcms.forms import CoderedFormField
 from coderedcms.forms import CoderedFormField
-from coderedcms.models import (
+from coderedcms.models import CoderedArticleIndexPage
-    CoderedArticlePage,
+from coderedcms.models import CoderedArticlePage
-    CoderedArticleIndexPage,
+from coderedcms.models import CoderedEmail
-    CoderedEventIndexPage,
+from coderedcms.models import CoderedEventIndexPage
-    CoderedEventPage,
+from coderedcms.models import CoderedEventOccurrence
-    CoderedEventOccurrence,
+from coderedcms.models import CoderedEventPage
-    CoderedEmail,
+from coderedcms.models import CoderedFormPage
-    CoderedFormPage,
+from coderedcms.models import CoderedLocationIndexPage
-    CoderedLocationIndexPage,
+from coderedcms.models import CoderedLocationPage
-    CoderedLocationPage,
+from coderedcms.models import CoderedStreamFormPage
-    CoderedStreamFormPage,
+from coderedcms.models import CoderedWebPage
-    CoderedWebPage,
+from coderedcms.models.page_models import CoderedPage
-)
 
 
 
 
 class ArticlePage(CoderedArticlePage):
 class ArticlePage(CoderedArticlePage):

+ 5 - 1
coderedcms/tests/urls.py

@@ -1,10 +1,14 @@
-from django.urls import include, path, re_path
 from django.contrib import admin
 from django.contrib import admin
+from django.urls import include
+from django.urls import path
+from django.urls import re_path
 from wagtail.documents import urls as wagtaildocs_urls
 from wagtail.documents import urls as wagtaildocs_urls
+
 from coderedcms import admin_urls as crx_admin_urls
 from coderedcms import admin_urls as crx_admin_urls
 from coderedcms import search_urls as crx_search_urls
 from coderedcms import search_urls as crx_search_urls
 from coderedcms import urls as crx_urls
 from coderedcms import urls as crx_urls
 
 
+
 urlpatterns = [
 urlpatterns = [
     path("django-admin/", admin.site.urls),
     path("django-admin/", admin.site.urls),
     path("admin/", include(crx_admin_urls)),
     path("admin/", include(crx_admin_urls)),

+ 12 - 11
coderedcms/urls.py

@@ -1,16 +1,17 @@
-from django.urls import include, path, re_path
+from django.urls import include
-from wagtail.contrib.sitemaps.views import sitemap
+from django.urls import path
+from django.urls import re_path
 from wagtail import urls as wagtailcore_urls
 from wagtail import urls as wagtailcore_urls
+from wagtail.contrib.sitemaps.views import sitemap
+
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
-from coderedcms.views import (
+from coderedcms.views import event_generate_ical_for_calendar
-    event_generate_ical_for_calendar,
+from coderedcms.views import event_generate_recurring_ical_for_event
-    event_generate_recurring_ical_for_event,
+from coderedcms.views import event_generate_single_ical_for_event
-    event_generate_single_ical_for_event,
+from coderedcms.views import event_get_calendar_events
-    event_get_calendar_events,
+from coderedcms.views import favicon
-    favicon,
+from coderedcms.views import robots
-    robots,
+from coderedcms.views import serve_protected_file
-    serve_protected_file,
-)
 
 
 
 
 urlpatterns = [
 urlpatterns = [

+ 1 - 1
coderedcms/utils.py

@@ -1,5 +1,5 @@
-from django.core.validators import URLValidator
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
+from django.core.validators import URLValidator
 from django.utils.html import mark_safe
 from django.utils.html import mark_safe
 
 
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings

+ 23 - 22
coderedcms/views.py

@@ -1,37 +1,38 @@
 import mimetypes
 import mimetypes
 import os
 import os
 from datetime import datetime
 from datetime import datetime
-from django.http import (
+
-    Http404,
+from django.contrib.auth.decorators import login_required
-    HttpResponse,
+from django.contrib.auth.decorators import permission_required
-    HttpResponsePermanentRedirect,
-    JsonResponse,
-)
-from django.contrib.auth.decorators import login_required, permission_required
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
-from django.core.paginator import (
+from django.core.paginator import EmptyPage
-    Paginator,
+from django.core.paginator import InvalidPage
-    InvalidPage,
+from django.core.paginator import PageNotAnInteger
-    EmptyPage,
+from django.core.paginator import Paginator
-    PageNotAnInteger,
+from django.http import Http404
-)
+from django.http import HttpResponse
-from django.shortcuts import redirect, render
+from django.http import HttpResponsePermanentRedirect
+from django.http import JsonResponse
+from django.shortcuts import redirect
+from django.shortcuts import render
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.translation import ngettext, gettext_lazy as _
+from django.utils.translation import gettext_lazy as _
+from django.utils.translation import ngettext
 from django.views.decorators.http import require_POST
 from django.views.decorators.http import require_POST
 from icalendar import Calendar
 from icalendar import Calendar
 from wagtail.admin import messages
 from wagtail.admin import messages
-from wagtail.models import Page, get_page_models
+from wagtail.models import Page
+from wagtail.models import get_page_models
 from wagtail.search.backends import get_search_backend
 from wagtail.search.backends import get_search_backend
 from wagtail.search.backends.database.mysql.mysql import MySQLSearchBackend
 from wagtail.search.backends.database.mysql.mysql import MySQLSearchBackend
+
 from coderedcms import utils
 from coderedcms import utils
 from coderedcms.forms import SearchForm
 from coderedcms.forms import SearchForm
-from coderedcms.models import CoderedPage, LayoutSettings
+from coderedcms.importexport import ImportPagesFromCSVFileForm
-from coderedcms.importexport import (
+from coderedcms.importexport import convert_csv_to_json
-    convert_csv_to_json,
+from coderedcms.importexport import import_pages
-    import_pages,
+from coderedcms.models import CoderedPage
-    ImportPagesFromCSVFileForm,
+from coderedcms.models import LayoutSettings
-)
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
 from coderedcms.templatetags.coderedcms_tags import get_name_of_class
 from coderedcms.templatetags.coderedcms_tags import get_name_of_class
 
 

+ 12 - 14
coderedcms/wagtail_flexible_forms/blocks.py

@@ -1,22 +1,20 @@
+from anyascii import anyascii
 from django import forms
 from django import forms
 from django.db.models import BLANK_CHOICE_DASH
 from django.db.models import BLANK_CHOICE_DASH
 from django.utils.dateparse import parse_datetime
 from django.utils.dateparse import parse_datetime
 from django.utils.text import slugify
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-from anyascii import anyascii
+from wagtail.blocks import BooleanBlock
-from wagtail.blocks import (
+from wagtail.blocks import CharBlock
-    StructBlock,
+from wagtail.blocks import ChoiceBlock
-    TextBlock,
+from wagtail.blocks import DateBlock
-    CharBlock,
+from wagtail.blocks import DateTimeBlock
-    BooleanBlock,
+from wagtail.blocks import ListBlock
-    ListBlock,
+from wagtail.blocks import RichTextBlock
-    StreamBlock,
+from wagtail.blocks import StreamBlock
-    DateBlock,
+from wagtail.blocks import StructBlock
-    TimeBlock,
+from wagtail.blocks import TextBlock
-    DateTimeBlock,
+from wagtail.blocks import TimeBlock
-    ChoiceBlock,
-    RichTextBlock,
-)
 
 
 
 
 class FormFieldBlock(StructBlock):
 class FormFieldBlock(StructBlock):

+ 0 - 1
coderedcms/wagtail_flexible_forms/edit_handlers.py

@@ -1,7 +1,6 @@
 from django.template.loader import render_to_string
 from django.template.loader import render_to_string
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
-
 from wagtail.admin.panels import EditHandler
 from wagtail.admin.panels import EditHandler
 
 
 
 

+ 31 - 26
coderedcms/wagtail_flexible_forms/models.py

@@ -1,12 +1,11 @@
+import datetime
+import json
+import os
 from collections import OrderedDict
 from collections import OrderedDict
 from importlib import import_module
 from importlib import import_module
 from itertools import zip_longest
 from itertools import zip_longest
-import json
-import os
 from pathlib import Path
 from pathlib import Path
 
 
-from PIL import Image
-import datetime
 from django.apps import apps
 from django.apps import apps
 from django.conf import settings
 from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
@@ -14,32 +13,35 @@ from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.core.files.storage import default_storage
 from django.core.files.storage import default_storage
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.serializers.json import DjangoJSONEncoder
-from django.db.models import (
+from django.db.models import CASCADE
-    CharField,
+from django.db.models import PROTECT
-    TextField,
+from django.db.models import CharField
-    DateTimeField,
+from django.db.models import DateTimeField
-    Model,
+from django.db.models import ForeignKey
-    ForeignKey,
+from django.db.models import Model
-    PROTECT,
+from django.db.models import QuerySet
-    CASCADE,
+from django.db.models import TextField
-    QuerySet,
-)
 from django.db.models.fields.files import FieldFile
 from django.db.models.fields.files import FieldFile
 from django.db.models.signals import post_delete
 from django.db.models.signals import post_delete
 from django.dispatch import receiver
 from django.dispatch import receiver
-from django.forms import Form, ImageField, FileField, URLField, EmailField
+from django.forms import EmailField
+from django.forms import FileField
+from django.forms import Form
+from django.forms import ImageField
+from django.forms import URLField
 from django.http import HttpResponseRedirect
 from django.http import HttpResponseRedirect
 from django.template.response import TemplateResponse
 from django.template.response import TemplateResponse
-from django.utils.safestring import SafeData, mark_safe
+from django.utils.safestring import SafeData
+from django.utils.safestring import mark_safe
 from django.utils.timezone import now
 from django.utils.timezone import now
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-from wagtail.contrib.forms.models import (
+from PIL import Image
-    AbstractForm,
+from wagtail.contrib.forms.models import AbstractEmailForm
-    AbstractEmailForm,
+from wagtail.contrib.forms.models import AbstractForm
-    AbstractFormSubmission,
+from wagtail.contrib.forms.models import AbstractFormSubmission
-)
 
 
-from .blocks import FormStepBlock, FormFieldBlock
+from .blocks import FormFieldBlock
+from .blocks import FormStepBlock
 
 
 
 
 class Step:
 class Step:
@@ -513,10 +515,13 @@ class SessionFormSubmission(AbstractFormSubmission):
             self.form_page.get_data_fields(by_step=True),
             self.form_page.get_data_fields(by_step=True),
             self.get_steps_data(raw=raw),
             self.get_steps_data(raw=raw),
         ):
         ):
-            yield step, [
+            yield (
-                (field_name, field_label, step_data[field_name])
+                step,
-                for field_name, field_label in step_data_fields
+                [
-            ]
+                    (field_name, field_label, step_data[field_name])
+                    for field_name, field_label in step_data_fields
+                ],
+            )
 
 
 
 
 @receiver(post_delete, sender=SessionFormSubmission)
 @receiver(post_delete, sender=SessionFormSubmission)

+ 4 - 3
coderedcms/wagtail_hooks.py

@@ -1,13 +1,13 @@
 import mimetypes
 import mimetypes
 
 
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
-from django.templatetags.static import static
 from django.http.response import HttpResponse
 from django.http.response import HttpResponse
+from django.templatetags.static import static
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.html import format_html
 from django.utils.html import format_html
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
-from wagtail.admin.menu import MenuItem
 from wagtail import hooks
 from wagtail import hooks
+from wagtail.admin.menu import MenuItem
 from wagtail.models import get_page_models
 from wagtail.models import get_page_models
 from wagtail.permission_policies.pages import PagePermissionPolicy
 from wagtail.permission_policies.pages import PagePermissionPolicy
 from wagtailcache.cache import clear_cache
 from wagtailcache.cache import clear_cache
@@ -87,9 +87,10 @@ def crx_forms(user, editable_forms):
     of its existence. Essentially this is a fork of wagtail.contrib.forms.get_forms_for_user()
     of its existence. Essentially this is a fork of wagtail.contrib.forms.get_forms_for_user()
     and wagtail.contrib.forms.get_form_types()
     and wagtail.contrib.forms.get_form_types()
     """
     """
-    from coderedcms.models import CoderedFormMixin
     from wagtail.contrib.forms.models import FormMixin
     from wagtail.contrib.forms.models import FormMixin
 
 
+    from coderedcms.models import CoderedFormMixin
+
     form_models = [
     form_models = [
         model
         model
         for model in get_page_models()
         for model in get_page_models()

+ 4 - 5
docs/contributing/index.rst

@@ -206,18 +206,17 @@ For example, here is how you would add tests for a new abstract page type,
 Static Analysis
 Static Analysis
 ---------------
 ---------------
 
 
-All code should be formatted with ``black`` before committing:
+All code should be formatted with ``ruff`` before committing:
 
 
 .. code-block:: console
 .. code-block:: console
 
 
-    $ black .
+    $ ruff format .
 
 
-Flake8 is used to check for syntax and style errors. To analyze the entire
+Ruff is also used to check for syntax and style errors. To analyze the entire codebase, run:
-codebase, run:
 
 
 .. code-block:: console
 .. code-block:: console
 
 
-    $ flake8 .
+    $ ruff check --fix .
 
 
 
 
 Contributor Guidelines
 Contributor Guidelines

+ 6 - 0
docs/releases/v4.0.0.rst

@@ -38,6 +38,12 @@ Bug fixes
 * Fix template error (Server 500 error) when a page is deleted which is referenced in a Page Preview block.
 * Fix template error (Server 500 error) when a page is deleted which is referenced in a Page Preview block.
 
 
 
 
+Maintenance
+-----------
+
+* Ruff is now used for formatting and linting in place of black and flake8.
+
+
 Upgrade considerations
 Upgrade considerations
 ----------------------
 ----------------------
 
 

+ 37 - 1
pyproject.toml

@@ -1,6 +1,5 @@
 [tool.black]
 [tool.black]
 line-length = 80
 line-length = 80
-target-version = ['py37', 'py38', 'py39', 'py310']
 exclude = '''
 exclude = '''
 (
 (
   /(
   /(
@@ -21,3 +20,40 @@ exclude = '''
   )/
   )/
 )
 )
 '''
 '''
+
+[tool.codespell]
+skip = 'migrations,vendor,_build,*.css.map,*.jpg,*.png,*.pyc'
+ignore-words-list = 'assertIn'
+
+[tool.django-stubs]
+django_settings_module = "coderedcms.tests.settings"
+
+[tool.mypy]
+ignore_missing_imports = true
+plugins = ["mypy_django_plugin.main"]
+exclude = [
+    '^\..*',
+    'migrations',
+    'node_modules',
+    'venv',
+    'testproject',
+]
+
+[tool.pytest.ini_options]
+DJANGO_SETTINGS_MODULE = "coderedcms.tests.settings"
+addopts = "--cov --cov-report html --cov-report xml --junitxml junit/test-results.xml"
+python_files = "tests.py test_*.py"
+junit_family = "xunit2"
+junit_suite_name = "coderedcms"
+
+[tool.ruff]
+extend-exclude = ["migrations", "testproject"]
+line-length = 80
+
+[tool.ruff.lint]
+extend-select = ["I"]
+
+[tool.ruff.lint.isort]
+case-sensitive = false
+force-single-line = true
+lines-after-imports = 2

+ 2 - 2
requirements-ci.txt

@@ -2,10 +2,10 @@
 -e .
 -e .
 
 
 # Requirements, in addition to coderedcms, needed for CI runner.
 # Requirements, in addition to coderedcms, needed for CI runner.
-black==24.2.0
 codespell
 codespell
-flake8
+pytest
 pytest-cov
 pytest-cov
 pytest-django
 pytest-django
+ruff
 sphinx
 sphinx
 sphinx-wagtail-theme
 sphinx-wagtail-theme

+ 0 - 10
setup.cfg

@@ -1,10 +0,0 @@
-[flake8]
-exclude = .venv/*,build/*,*/migrations/*,schema.py
-max-line-length = 100
-
-[tool:pytest]
-DJANGO_SETTINGS_MODULE = coderedcms.tests.settings
-addopts = --cov coderedcms --cov-report html --cov-report xml --junitxml junit/test-results.xml
-python_files = tests.py test_*.py
-junit_family = xunit2
-junit_suite_name = coderedcms

+ 3 - 0
setup.py

@@ -1,7 +1,10 @@
 import os
 import os
+
 from setuptools import setup
 from setuptools import setup
+
 from coderedcms import __version__
 from coderedcms import __version__
 
 
+
 with open(
 with open(
     os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8"
     os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8"
 ) as readme:
 ) as readme: