浏览代码

Static analysis code cleanup and CI tooling (#203)

Kevin Cummings 5 年之前
父节点
当前提交
560432b497
共有 41 个文件被更改,包括 665 次插入318 次删除
  1. 86 48
      azure-pipelines.yml
  2. 31 0
      ci/run_artifacts.ps1
  3. 20 0
      ci/run_autopep8.ps1
  4. 20 0
      ci/run_black.ps1
  5. 2 1
      coderedcms/admin_urls.py
  6. 5 3
      coderedcms/api/mailchimp.py
  7. 11 4
      coderedcms/bin/coderedcms.py
  8. 67 16
      coderedcms/blocks/__init__.py
  9. 10 9
      coderedcms/blocks/base_blocks.py
  10. 4 2
      coderedcms/blocks/content_blocks.py
  11. 11 4
      coderedcms/blocks/html_blocks.py
  12. 5 3
      coderedcms/blocks/layout_blocks.py
  13. 6 2
      coderedcms/blocks/metadata_blocks.py
  14. 10 8
      coderedcms/blocks/stream_form_blocks.py
  15. 6 4
      coderedcms/blocks/tests/test_blocks.py
  16. 2 1
      coderedcms/fields.py
  17. 17 7
      coderedcms/forms.py
  18. 11 14
      coderedcms/importexport.py
  19. 4 4
      coderedcms/models/__init__.py
  20. 20 16
      coderedcms/models/integration_models.py
  21. 114 73
      coderedcms/models/page_models.py
  22. 36 7
      coderedcms/models/snippet_models.py
  23. 4 3
      coderedcms/models/tests/test_page_models.py
  24. 25 9
      coderedcms/models/wagtailsettings_models.py
  25. 2 2
      coderedcms/project_template/project_name/settings/dev.py
  26. 3 3
      coderedcms/project_template/project_name/settings/prod.py
  27. 1 1
      coderedcms/project_template/project_name/urls.py
  28. 11 7
      coderedcms/settings.py
  29. 1 1
      coderedcms/templatetags/coderedcms_tags.py
  30. 2 6
      coderedcms/tests/settings.py
  31. 0 1
      coderedcms/tests/urls.py
  32. 11 5
      coderedcms/urls.py
  33. 11 1
      coderedcms/utils.py
  34. 19 14
      coderedcms/views.py
  35. 5 8
      coderedcms/wagtail_flexible_forms/models.py
  36. 5 5
      coderedcms/wagtail_flexible_forms/wagtail_hooks.py
  37. 20 8
      coderedcms/wagtail_hooks.py
  38. 2 2
      docs/conf.py
  39. 26 0
      pyproject.toml
  40. 2 2
      setup.cfg
  41. 17 14
      setup.py

+ 86 - 48
azure-pipelines.yml

@@ -3,7 +3,7 @@
 # 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
 
 
-# NTOES:
+# NOTES:
 #
 #
 # Display name of each step should be prefixed with one of the following:
 # Display name of each step should be prefixed with one of the following:
 #   CR-QC: for quality control measures.
 #   CR-QC: for quality control measures.
@@ -14,53 +14,91 @@
 # Use PowerShell Core for any scripts so they are re-usable across windows/mac/linux.
 # Use PowerShell Core for any scripts so they are re-usable across windows/mac/linux.
 #
 #
 
 
+
 trigger:
 trigger:
   - master
   - master
 
 
-pool:
-  vmImage: 'ubuntu-latest'
-strategy:
-  matrix:
-    py3.5:
-      PYTHON_VERSION: '3.5'
-    py3.6:
-      PYTHON_VERSION: '3.6'
-    py3.7:
-      PYTHON_VERSION: '3.7'
-
-steps:
-- task: UsePythonVersion@0
-  displayName: 'Use Python version'
-  inputs:
-    versionSpec: '$(PYTHON_VERSION)'
-    architecture: 'x64'
-
-- script: |
-    python -m pip install -e ./[ci]
-  displayName: 'CR-QC: Install coderedcms from local repo'
-
-- script: |
-    coderedcms start testproject --name="Test Project" --domain="www.example.com"
-  displayName: 'CR-QC: Create starter project from template'
-
-- script: |
-    pytest coderedcms/ --ds=coderedcms.tests.settings --junitxml=junit/test-results.xml --cov=coderedcms --cov-report=xml --cov-report=html
-  displayName: 'CR-QC: Run unit tests'
-
-- pwsh: |
-    & ci/run_flake8.ps1
-  displayName: 'CR-QC: Static analysis'
-
-- task: PublishTestResults@2
-  displayName: 'Publish unit test report'
-  condition: succeededOrFailed()
-  inputs:
-    testResultsFiles: '**/test-*.xml'
-    testRunTitle: 'Publish test results for Python $(python.version)'
-
-- task: PublishCodeCoverageResults@1
-  displayName: 'Publish code coverage report'
-  condition: succeededOrFailed()
-  inputs:
-    codeCoverageTool: Cobertura
-    summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage.xml'
+jobs:
+- job: tests
+  displayName: Unit Tests
+  pool:
+    vmImage: 'ubuntu-latest'
+  strategy:
+    matrix:
+      py3.5:
+        PYTHON_VERSION: '3.5'
+      py3.6:
+        PYTHON_VERSION: '3.6'
+      py3.7:
+        PYTHON_VERSION: '3.7'
+  
+  steps:
+  - task: UsePythonVersion@0
+    displayName: 'Use Python version'
+    inputs:
+      versionSpec: '$(PYTHON_VERSION)'
+      architecture: 'x64'
+
+  - script: |
+      python -m pip install -e ./[ci_tests]
+    displayName: 'CR-QC: Install coderedcms from local repo'
+
+  - script: |
+      coderedcms start testproject --name="Test Project" --domain="www.example.com"
+    displayName: 'CR-QC: Create starter project from template'
+
+  - script: |
+      pytest coderedcms/ --ds=coderedcms.tests.settings --junitxml=junit/test-results.xml --cov=coderedcms --cov-report=xml --cov-report=html
+    displayName: 'CR-QC: Run unit tests'
+
+  - task: PublishTestResults@2
+    displayName: 'Publish unit test report'
+    condition: succeededOrFailed()
+    inputs:
+      testResultsFiles: '**/test-*.xml'
+      testRunTitle: 'Publish test results for Python $(python.version)'
+
+  - task: PublishCodeCoverageResults@1
+    displayName: 'Publish code coverage report'
+    condition: succeededOrFailed()
+    inputs:
+      codeCoverageTool: Cobertura
+      summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage.xml'
+
+  - task: DownloadPipelineArtifact@2
+    displayName: 'Download code coverage from latest build'
+    inputs:
+      source: 'specific'
+      path: '$(Agent.WorkFolder)/artifacts'
+      project: 'cc52b8d8-3ae5-466e-b771-56f3b4749f1f'
+      pipeline: 1
+      runVersion: 'latestFromBranch'
+      runBranch: 'refs/heads/master'
+
+  - pwsh: |
+      & ci/run_artifacts.ps1
+    displayName: 'Compare code coverage to master'
+
+- job: style
+  displayName: Static Analysis
+  pool:
+    vmImage: 'ubuntu-latest'
+
+  steps:
+  - task: UsePythonVersion@0
+    displayName: 'Use Python version'
+    inputs:
+      versionSpec: '3.7'
+      architecture: 'x64'
+
+  - script: |
+      python -m pip install -e ./[ci_style]
+    displayName: 'CR-QC: Install coderedcms from local repo'
+
+  - script: |
+      coderedcms start testproject
+    displayName: 'CR-QC: Generate a test project'
+
+  - pwsh: |
+      & ci/run_flake8.ps1
+    displayName: 'CR-QC: Static analysis (flake8)'

+ 31 - 0
ci/run_artifacts.ps1

@@ -0,0 +1,31 @@
+if (Test-Path -Path "/home/vsts/work/artifacts/Code Coverage Report_*/summary*/coverage.xml") {
+    [xml]$MasterXML = Get-Content "/home/vsts/work/artifacts/Code Coverage Report_*/summary*/coverage.xml"
+} else {
+    Write-Host "No code coverage from previous build. Exiting pipeline." -ForegroundColor Red
+    exit 1
+}
+
+[xml]$BranchXML = Get-Content .\coverage.xml
+
+$masterlinerate = [math]::Abs([math]::Round([decimal]$MasterXML.coverage.'line-rate' * 100, 2))
+$branchlinerate = [math]::Abs([math]::Round([decimal]$BranchXML.coverage.'line-rate' * 100, 2))
+
+Write-Output "Old line coverage rate: $masterlinerate%"
+Write-Output "New line coverage rate: $branchlinerate%"
+
+if ($masterlinerate -eq 0) {
+    $change = "Infinite"
+} else {
+    $change = [math]::Abs([math]::Round((($branchlinerate - $masterlinerate) / $masterlinerate) * 100, 2))
+}
+
+if ($branchlinerate -gt $masterlinerate) {
+    Write-Host "Code coverage has increased by $change%. Build passed." -ForegroundColor Green
+    exit 0
+} elseif ($branchlinerate -eq $masterlinerate) {
+    Write-Host "Code coverage has not changed. Build passed." -ForegroundColor Green
+    exit 0
+} else {
+    Write-Host "Code coverage as decreased by $change%. Code coverage must be greater than or equal to the previous build to pass." -ForegroundColor Red
+    exit 2
+}

+ 20 - 0
ci/run_autopep8.ps1

@@ -0,0 +1,20 @@
+$ExitCode = 0
+$GitDiff = git diff --name-only origin/master
+$GitDiffPep = Write-Output $GitDiff | Select-String -Pattern ".*\.py" | Select-String -NotMatch ".*/project_template/.*"
+# If there is no diff between master, then run everything.
+if ( $GitDiffPep -eq $null ) {
+    autopep8 -r --diff coderedcms/
+    if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+}
+# Else run just the diff.
+else {
+    autopep8 -r --diff $GitDiffPep
+    if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+    # If the project_template changed, then run the testproject too.
+    $GitDiffTempl = Write-Output $GitDiff | Select-String -Pattern ".*/project_template/.*"
+    if ( $GitDiffTempl -ne $null ) {
+        #autopep8 -r --diff testproject/
+        if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+    }
+}
+exit $ExitCode

+ 20 - 0
ci/run_black.ps1

@@ -0,0 +1,20 @@
+$ExitCode = 0
+$GitDiff = git diff --name-only origin/master
+$GitDiffBlack = Write-Output $GitDiff | Select-String -Pattern ".*\.py" | Select-String -NotMatch ".*/project_template/.*"
+# If there is no diff between master, then black everything.
+if ( $GitDiffBlack -eq $null ) {
+    black --check .
+    if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+}
+# Else black just the diff.
+else {
+    black --check $GitDiffBlack
+    if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+    # If the project_template changed, then black the testproject too.
+    $GitDiffTempl = Write-Output $GitDiff | Select-String -Pattern ".*/project_template/.*"
+    if ( $GitDiffTempl -ne $null ) {
+        black --check testproject/
+        if ($LastExitCode -ne 0) { $ExitCode = $LastExitCode }
+    }
+}
+exit $ExitCode

+ 2 - 1
coderedcms/admin_urls.py

@@ -5,7 +5,8 @@ from coderedcms.views import import_pages_from_csv_file
 
 
 
 
 urlpatterns = [
 urlpatterns = [
-    path('codered/import-export/import_from_csv/', import_pages_from_csv_file, name="import_from_csv"),
+    path('codered/import-export/import_from_csv/',
+         import_pages_from_csv_file, name="import_from_csv"),
     re_path(r'', include(wagtailadmin_urls)),
     re_path(r'', include(wagtailadmin_urls)),
     re_path(r'', include(wagtailimportexport_urls)),
     re_path(r'', include(wagtailimportexport_urls)),
 ]
 ]

+ 5 - 3
coderedcms/api/mailchimp.py

@@ -3,6 +3,7 @@ from coderedcms.models.wagtailsettings_models import MailchimpApiSettings
 
 
 import requests
 import requests
 
 
+
 class MailchimpApi:
 class MailchimpApi:
     user_string = "Website"
     user_string = "Website"
     proto_base_url = "https://{0}.api.mailchimp.com/3.0/"
     proto_base_url = "https://{0}.api.mailchimp.com/3.0/"
@@ -40,17 +41,18 @@ class MailchimpApi:
         return json_response
         return json_response
 
 
     def get_merge_fields_for_list(self, list_id):
     def get_merge_fields_for_list(self, list_id):
-        endpoint = "lists/{0}/merge-fields?fields=merge_fields.tag,merge_fields.merge_id,merge_fields.name".format(list_id)
+        endpoint = "lists/{0}/merge-fields?fields=merge_fields.tag,merge_fields.merge_id,merge_fields.name".format(list_id)  # noqa
         json_response = self._get(endpoint)
         json_response = self._get(endpoint)
         return json_response
         return json_response
 
 
     def get_interest_categories_for_list(self, list_id):
     def get_interest_categories_for_list(self, list_id):
-        endpoint = "lists/{0}/interest-categories?fields=categories.id,categories.title".format(list_id)
+        endpoint = "lists/{0}/interest-categories?fields=categories.id,categories.title".format(
+            list_id)
         json_response = self._get(endpoint)
         json_response = self._get(endpoint)
         return json_response
         return json_response
 
 
     def get_interests_for_interest_category(self, list_id, interest_category_id):
     def get_interests_for_interest_category(self, list_id, interest_category_id):
-        endpoint = "lists/{0}/interest-categories/{1}/interests?fields=interests.id,interests.name".format(list_id, interest_category_id)
+        endpoint = "lists/{0}/interest-categories/{1}/interests?fields=interests.id,interests.name".format(list_id, interest_category_id)  # noqa
         json_response = self._get(endpoint)
         json_response = self._get(endpoint)
         return json_response
         return json_response
 
 

+ 11 - 4
coderedcms/bin/coderedcms.py

@@ -2,7 +2,6 @@
 import os
 import os
 import sys
 import sys
 
 
-from django.core.management import ManagementUtility
 from django.core.management.templates import TemplateCommand
 from django.core.management.templates import TemplateCommand
 from django.core.management.utils import get_random_secret_key
 from django.core.management.utils import get_random_secret_key
 
 
@@ -11,7 +10,9 @@ CURRENT_PYTHON = sys.version_info[:2]
 REQUIRED_PYTHON = (3, 4)
 REQUIRED_PYTHON = (3, 4)
 
 
 if CURRENT_PYTHON < REQUIRED_PYTHON:
 if CURRENT_PYTHON < REQUIRED_PYTHON:
-    sys.stderr.write("This version of Wagtail requires Python {}.{} or above - you are running {}.{}\n".format(*(REQUIRED_PYTHON + CURRENT_PYTHON)))
+    sys.stderr.write(
+        "This version of Wagtail requires Python {}.{} or above - you are running {}.{}\n".format(*(REQUIRED_PYTHON + CURRENT_PYTHON))  # noqa
+    )
     sys.exit(1)
     sys.exit(1)
 
 
 
 
@@ -23,8 +24,14 @@ class CreateProject(TemplateCommand):
     missing_args_message = "You must provide a project name."
     missing_args_message = "You must provide a project name."
 
 
     def add_arguments(self, parser):
     def add_arguments(self, parser):
-        parser.add_argument('--sitename', help='Human readable name of your website or brand, e.g. "Mega Corp Inc."')
-        parser.add_argument('--domain', help='Domain that will be used for your website in production, e.g. "www.example.com"')
+        parser.add_argument(
+            '--sitename',
+            help='Human readable name of your website or brand, e.g. "Mega Corp Inc."'
+        )
+        parser.add_argument(
+            '--domain',
+            help='Domain that will be used for your website in production, e.g. "www.example.com"'
+        )
         super().add_arguments(parser)
         super().add_arguments(parser)
 
 
     def handle(self, **options):
     def handle(self, **options):

+ 67 - 16
coderedcms/blocks/__init__.py

@@ -5,18 +5,69 @@ single `blocks` module.
 """
 """
 
 
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
-from wagtail.core.blocks import CharBlock, StreamBlock, StructBlock
-
-from coderedcms.wagtail_flexible_forms.blocks import FormStepBlock, FormStepsBlock
-
-from .stream_form_blocks import * #noqa
-from .base_blocks import * #noqa
-from .html_blocks import * #noqa
-from .metadata_blocks import * #noqa
-from .content_blocks import * #noqa
-from .layout_blocks import * #noqa
 
 
+from wagtail.core import blocks
 
 
+from .stream_form_blocks import (
+    CoderedStreamFormCharFieldBlock,
+    CoderedStreamFormCheckboxesFieldBlock,
+    CoderedStreamFormCheckboxFieldBlock,
+    CoderedStreamFormDateFieldBlock,
+    CoderedStreamFormDateTimeFieldBlock,
+    CoderedStreamFormDropdownFieldBlock,
+    CoderedStreamFormFileFieldBlock,
+    CoderedStreamFormImageFieldBlock,
+    CoderedStreamFormNumberFieldBlock,
+    CoderedStreamFormRadioButtonsFieldBlock,
+    CoderedStreamFormStepBlock,
+    CoderedStreamFormTextFieldBlock,
+    CoderedStreamFormTimeFieldBlock
+)
+from .html_blocks import (
+    ButtonBlock,
+    EmbedGoogleMapBlock,
+    ImageBlock,
+    ImageLinkBlock,
+    DownloadBlock,
+    EmbedVideoBlock,
+    PageListBlock,
+    PagePreviewBlock,
+    QuoteBlock,
+    RichTextBlock,
+    TableBlock
+)
+from .content_blocks import (  # noqa
+    CardBlock,
+    CarouselBlock,
+    ContentWallBlock,
+    ImageGalleryBlock,
+    ModalBlock,
+    NavDocumentLinkWithSubLinkBlock,
+    NavExternalLinkWithSubLinkBlock,
+    NavPageLinkWithSubLinkBlock,
+    PriceListBlock,
+    ReusableContentBlock
+)
+from .layout_blocks import (
+    CardGridBlock,
+    GridBlock,
+    HeroBlock
+)
+from .metadata_blocks import (  # noqa
+    OpenHoursBlock,
+    StructuredDataActionBlock
+)
+from .base_blocks import (  # noqa
+    BaseBlock,
+    BaseLayoutBlock,
+    BaseLinkBlock,
+    ClassifierTermChooserBlock,
+    CoderedAdvColumnSettings,
+    CoderedAdvSettings,
+    CoderedAdvTrackingSettings,
+    CollectionChooserBlock,
+    MultiSelectBlock
+)
 
 
 # Collections of blocks commonly used together.
 # Collections of blocks commonly used together.
 
 
@@ -59,14 +110,14 @@ LAYOUT_STREAMBLOCKS = [
     ('hero', HeroBlock([
     ('hero', HeroBlock([
         ('row', GridBlock(CONTENT_STREAMBLOCKS)),
         ('row', GridBlock(CONTENT_STREAMBLOCKS)),
         ('cardgrid', CardGridBlock([
         ('cardgrid', CardGridBlock([
-            ('card', CardBlock()),])
-        ),
-        ('html', blocks.RawHTMLBlock(icon='code', classname='monospace', label=_('HTML'))),])
-    ),
+            ('card', CardBlock()),
+        ])),
+        ('html', blocks.RawHTMLBlock(icon='code', classname='monospace', label=_('HTML'))),
+    ])),
     ('row', GridBlock(CONTENT_STREAMBLOCKS)),
     ('row', GridBlock(CONTENT_STREAMBLOCKS)),
     ('cardgrid', CardGridBlock([
     ('cardgrid', CardGridBlock([
-        ('card', CardBlock()),])
-    ),
+        ('card', CardBlock()),
+    ])),
     ('html', blocks.RawHTMLBlock(icon='code', classname='monospace', label=_('HTML'))),
     ('html', blocks.RawHTMLBlock(icon='code', classname='monospace', label=_('HTML'))),
 ]
 ]
 
 

+ 10 - 9
coderedcms/blocks/base_blocks.py

@@ -21,6 +21,7 @@ class MultiSelectBlock(blocks.FieldBlock):
     Renders as MultipleChoiceField, used for adding checkboxes,
     Renders as MultipleChoiceField, used for adding checkboxes,
     radios, or multiselect inputs in the streamfield.
     radios, or multiselect inputs in the streamfield.
     """
     """
+
     def __init__(self, required=True, help_text=None, choices=None, widget=None, **kwargs):
     def __init__(self, required=True, help_text=None, choices=None, widget=None, **kwargs):
         self.field = forms.MultipleChoiceField(
         self.field = forms.MultipleChoiceField(
             required=required,
             required=required,
@@ -42,9 +43,9 @@ class ClassifierTermChooserBlock(blocks.FieldBlock):
     widget = forms.Select
     widget = forms.Select
 
 
     def __init__(self, required=False, label=None, help_text=None, *args, **kwargs):
     def __init__(self, required=False, label=None, help_text=None, *args, **kwargs):
-        self._required=required
-        self._help_text=help_text
-        self._label=label
+        self._required = required
+        self._help_text = help_text
+        self._label = label
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
     @cached_property
     @cached_property
@@ -86,9 +87,9 @@ class CollectionChooserBlock(blocks.FieldBlock):
     widget = forms.Select
     widget = forms.Select
 
 
     def __init__(self, required=False, label=None, help_text=None, *args, **kwargs):
     def __init__(self, required=False, label=None, help_text=None, *args, **kwargs):
-        self._required=required
-        self._help_text=help_text
-        self._label=label
+        self._required = required
+        self._help_text = help_text
+        self._label = label
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
     @cached_property
     @cached_property
@@ -207,7 +208,7 @@ class CoderedAdvColumnSettings(CoderedAdvSettings):
         default=cr_settings['FRONTEND_COL_BREAK_DEFAULT'],
         default=cr_settings['FRONTEND_COL_BREAK_DEFAULT'],
         required=False,
         required=False,
         verbose_name=_('Column Breakpoint'),
         verbose_name=_('Column Breakpoint'),
-        help_text=_('Screen size at which the column will expand horizontally or stack vertically.'),
+        help_text=_('Screen size at which the column will expand horizontally or stack vertically.'),  # noqa
     )
     )
 
 
 
 
@@ -227,7 +228,7 @@ class BaseBlock(blocks.StructBlock):
         """
         """
         klassname = self.__class__.__name__.lower()
         klassname = self.__class__.__name__.lower()
         choices = cr_settings['FRONTEND_TEMPLATES_BLOCKS'].get('*', ()) + \
         choices = cr_settings['FRONTEND_TEMPLATES_BLOCKS'].get('*', ()) + \
-                  cr_settings['FRONTEND_TEMPLATES_BLOCKS'].get(klassname, ())
+            cr_settings['FRONTEND_TEMPLATES_BLOCKS'].get(klassname, ())
 
 
         if not local_blocks:
         if not local_blocks:
             local_blocks = ()
             local_blocks = ()
@@ -285,6 +286,7 @@ class LinkStructValue(blocks.StructValue):
         else:
         else:
             return ext
             return ext
 
 
+
 class BaseLinkBlock(BaseBlock):
 class BaseLinkBlock(BaseBlock):
     """
     """
     Common attributes for creating a link within the CMS.
     Common attributes for creating a link within the CMS.
@@ -307,4 +309,3 @@ class BaseLinkBlock(BaseBlock):
 
 
     class Meta:
     class Meta:
         value_class = LinkStructValue
         value_class = LinkStructValue
-

+ 4 - 2
coderedcms/blocks/content_blocks.py

@@ -91,7 +91,7 @@ class ModalBlock(ButtonMixin, BaseLayoutBlock):
     )
     )
     footer = blocks.StreamBlock(
     footer = blocks.StreamBlock(
         [
         [
-            ('text', blocks.CharBlock(icon='fa-file-text-o', max_length=255, label=_('Simple Text'))),
+            ('text', blocks.CharBlock(icon='fa-file-text-o', max_length=255, label=_('Simple Text'))),  # noqa
             ('button', ButtonBlock()),
             ('button', ButtonBlock()),
         ],
         ],
         required=False,
         required=False,
@@ -263,8 +263,10 @@ class ContentWallBlock(BaseBlock):
         required=False,
         required=False,
         default=False,
         default=False,
         verbose_name=_('Show content walls on children pages?'),
         verbose_name=_('Show content walls on children pages?'),
-        help_text=_('If this is checked, the content walls will be displayed on all children pages of this page.')
+        help_text=_(
+            'If this is checked, the content walls will be displayed on all children pages of this page.')  # noqa
     )
     )
+
     class Meta:
     class Meta:
         icon = 'fa-stop'
         icon = 'fa-stop'
         label = _('Content Wall')
         label = _('Content Wall')

+ 11 - 4
coderedcms/blocks/html_blocks.py

@@ -79,7 +79,9 @@ class EmbedGoogleMapBlock(BaseBlock):
         required=False,
         required=False,
         default=14,
         default=14,
         label=_('Map zoom level'),
         label=_('Map zoom level'),
-        help_text=_('Requires API key to use zoom. 1: World, 5: Landmass/continent, 10: City, 15: Streets, 20: Buildings')
+        help_text=_(
+            "Requires API key to use zoom. 1: World, 5: Landmass/continent, 10: City, 15: Streets, 20: Buildings"  # noqa
+        )
     )
     )
 
 
     class Meta:
     class Meta:
@@ -199,7 +201,9 @@ class PageListBlock(BaseBlock):
     indexed_by = blocks.PageChooserBlock(
     indexed_by = blocks.PageChooserBlock(
         required=True,
         required=True,
         label=_('Parent page'),
         label=_('Parent page'),
-        help_text=_('Show a preview of pages that are children of the selected page. Uses ordering specified in the page’s LAYOUT tab.'),
+        help_text=_(
+            "Show a preview of pages that are children of the selected page. Uses ordering specified in the page’s LAYOUT tab."  # noqa
+        ),
     )
     )
     classified_by = ClassifierTermChooserBlock(
     classified_by = ClassifierTermChooserBlock(
         required=False,
         required=False,
@@ -233,9 +237,12 @@ class PageListBlock(BaseBlock):
             if value['classified_by']:
             if value['classified_by']:
                 try:
                 try:
                     pages = pages.filter(classifier_terms=value['classified_by'])
                     pages = pages.filter(classifier_terms=value['classified_by'])
-                except:
+                except AttributeError:
                     # `pages` is not a queryset, or is not a queryset of CoderedPage.
                     # `pages` is not a queryset, or is not a queryset of CoderedPage.
-                    logger.warning("Tried to filter by ClassifierTerm in PageListBlock, but <%s.%s ('%s')>.get_index_children() did not return a queryset or is not a queryset of CoderedPage models.", indexer._meta.app_label, indexer.__class__.__name__, indexer.title)
+                    logger.warning(
+                        "Tried to filter by ClassifierTerm in PageListBlock, but <%s.%s ('%s')>.get_index_children()  # noqadid not return a queryset or is not a queryset of CoderedPage models.",  # noqa
+                        indexer._meta.app_label, indexer.__class__.__name__, indexer.title
+                    )
         else:
         else:
             pages = indexer.get_children().live()
             pages = indexer.get_children().live()
 
 

+ 5 - 3
coderedcms/blocks/layout_blocks.py

@@ -12,7 +12,7 @@ from coderedcms.settings import cr_settings
 from .base_blocks import BaseLayoutBlock, CoderedAdvColumnSettings
 from .base_blocks import BaseLayoutBlock, CoderedAdvColumnSettings
 
 
 
 
-### Level 1 layout blocks
+# Level 1 layout blocks
 
 
 
 
 class ColumnBlock(BaseLayoutBlock):
 class ColumnBlock(BaseLayoutBlock):
@@ -50,11 +50,12 @@ class GridBlock(BaseLayoutBlock):
 
 
     def __init__(self, local_blocks=None, **kwargs):
     def __init__(self, local_blocks=None, **kwargs):
         super().__init__(
         super().__init__(
-            local_blocks = [
+            local_blocks=[
                 ('content', ColumnBlock(local_blocks))
                 ('content', ColumnBlock(local_blocks))
             ]
             ]
         )
         )
 
 
+
 class CardGridBlock(BaseLayoutBlock):
 class CardGridBlock(BaseLayoutBlock):
     """
     """
     Renders a row of cards.
     Renders a row of cards.
@@ -83,7 +84,8 @@ class HeroBlock(BaseLayoutBlock):
     is_parallax = blocks.BooleanBlock(
     is_parallax = blocks.BooleanBlock(
         required=False,
         required=False,
         label=_('Parallax Effect'),
         label=_('Parallax Effect'),
-        help_text=_('Background images scroll slower than foreground images, creating an illusion of depth.'),
+        help_text=_(
+            'Background images scroll slower than foreground images, creating an illusion of depth.'),  # noqa
     )
     )
     background_image = ImageChooserBlock(required=False)
     background_image = ImageChooserBlock(required=False)
     tile_image = blocks.BooleanBlock(
     tile_image = blocks.BooleanBlock(

+ 6 - 2
coderedcms/blocks/metadata_blocks.py

@@ -23,6 +23,7 @@ class OpenHoursValue(blocks.StructValue):
         """
         """
         return json.dumps(self['days'])
         return json.dumps(self['days'])
 
 
+
 class OpenHoursBlock(blocks.StructBlock):
 class OpenHoursBlock(blocks.StructBlock):
     """
     """
     Holds day and time combination for business open hours.
     Holds day and time combination for business open hours.
@@ -62,7 +63,8 @@ class StructuredDataActionBlock(blocks.StructBlock):
     target = blocks.URLBlock(verbose_name=_('Target URL'))
     target = blocks.URLBlock(verbose_name=_('Target URL'))
     language = blocks.CharBlock(
     language = blocks.CharBlock(
         verbose_name=_('Language'),
         verbose_name=_('Language'),
-        help_text=_('If the action is offered in multiple languages, create separate actions for each language.'),
+        help_text=_(
+            'If the action is offered in multiple languages, create separate actions for each language.'),  # noqa
         default='en-US'
         default='en-US'
     )
     )
     result_type = blocks.ChoiceBlock(
     result_type = blocks.ChoiceBlock(
@@ -80,7 +82,9 @@ class StructuredDataActionBlock(blocks.StructBlock):
         required=False,
         required=False,
         verbose_name=_('Additional action markup'),
         verbose_name=_('Additional action markup'),
         classname='monospace',
         classname='monospace',
-        help_text=_('Additional JSON-LD inserted into the Action dictionary. Must be properties of https://schema.org/Action.')
+        help_text=_(
+            "Additional JSON-LD inserted into the Action dictionary. Must be properties of https://schema.org/Action."  # noqa
+        )
     )
     )
 
 
     class Meta:
     class Meta:

+ 10 - 8
coderedcms/blocks/stream_form_blocks.py

@@ -4,9 +4,9 @@ from wagtail.core import blocks
 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.blocks.base_blocks import BaseBlock, CoderedAdvSettings
 from coderedcms.forms import (
 from coderedcms.forms import (
-    CoderedDateField, CoderedDateInput, 
-    CoderedDateTimeField, CoderedDateTimeInput, 
-    CoderedTimeField, CoderedTimeInput, 
+    CoderedDateField, CoderedDateInput,
+    CoderedDateTimeField, CoderedDateTimeInput,
+    CoderedTimeField, CoderedTimeInput,
     SecureFileField
     SecureFileField
 )
 )
 
 
@@ -17,19 +17,21 @@ class CoderedFormAdvSettings(CoderedAdvSettings):
         required=False,
         required=False,
         max_length=255,
         max_length=255,
         label=_('Condition Trigger ID'),
         label=_('Condition Trigger ID'),
-        help_text=_('The "Custom ID" of another field that that will trigger this field to be shown/hidden.')
+        help_text=_(
+            'The "Custom ID" of another field that that will trigger this field to be shown/hidden.')  # noqa
     )
     )
     condition_trigger_value = blocks.CharBlock(
     condition_trigger_value = blocks.CharBlock(
         required=False,
         required=False,
         max_length=255,
         max_length=255,
         label=_('Condition Trigger Value'),
         label=_('Condition Trigger Value'),
-        help_text=_('The value of the field in "Condition Trigger ID" that will trigger this field to be shown.')
+        help_text=_(
+            'The value of the field in "Condition Trigger ID" that will trigger this field to be shown.')  # noqa
     )
     )
 
 
 
 
 class FormBlockMixin(BaseBlock):
 class FormBlockMixin(BaseBlock):
     class Meta:
     class Meta:
-        abstract=True
+        abstract = True
 
 
     advsettings_class = CoderedFormAdvSettings
     advsettings_class = CoderedFormAdvSettings
 
 
@@ -84,7 +86,7 @@ class CoderedStreamFormDateFieldBlock(form_blocks.DateFieldBlock, FormBlockMixin
     class Meta:
     class Meta:
         label = _("Date")
         label = _("Date")
         icon = "fa-calendar"
         icon = "fa-calendar"
-    
+
     field_class = CoderedDateField
     field_class = CoderedDateField
     widget = CoderedDateInput
     widget = CoderedDateInput
 
 
@@ -126,7 +128,7 @@ class CoderedStreamFormStepBlock(form_blocks.FormStepBlock):
 
 
     def __init__(self, local_blocks=None, **kwargs):
     def __init__(self, local_blocks=None, **kwargs):
         super().__init__(
         super().__init__(
-            local_blocks = [
+            local_blocks=[
                 ('form_fields', blocks.StreamBlock(local_blocks))
                 ('form_fields', blocks.StreamBlock(local_blocks))
             ]
             ]
         )
         )

+ 6 - 4
coderedcms/blocks/tests/test_blocks.py

@@ -1,19 +1,21 @@
 from coderedcms.blocks import base_blocks
 from coderedcms.blocks import base_blocks
-from django.test import SimpleTestCase, TestCase
+from django.test import SimpleTestCase
 
 
 from wagtail.tests.utils import WagtailTestUtils
 from wagtail.tests.utils import WagtailTestUtils
 
 
+
 class TestMultiSelectBlock(WagtailTestUtils, SimpleTestCase):
 class TestMultiSelectBlock(WagtailTestUtils, SimpleTestCase):
     def test_render_single_choice(self):
     def test_render_single_choice(self):
-        block = base_blocks.MultiSelectBlock(choices=[('tea', 'Tea'), ('coffee', 'Coffee'), ('water', 'Water')])
+        block = base_blocks.MultiSelectBlock(
+            choices=[('tea', 'Tea'), ('coffee', 'Coffee'), ('water', 'Water')])
         html = block.render_form(['tea'])
         html = block.render_form(['tea'])
         self.assertInHTML('<option value="tea" selected>Tea</option>', html)
         self.assertInHTML('<option value="tea" selected>Tea</option>', html)
         self.assertTrue(html.count('selected'), 1)
         self.assertTrue(html.count('selected'), 1)
 
 
     def test_render_multi_choice(self):
     def test_render_multi_choice(self):
-        block = base_blocks.MultiSelectBlock(choices=[('tea', 'Tea'), ('coffee', 'Coffee'), ('water', 'Water')])
+        block = base_blocks.MultiSelectBlock(
+            choices=[('tea', 'Tea'), ('coffee', 'Coffee'), ('water', 'Water')])
         html = block.render_form(['coffee', 'tea'])
         html = block.render_form(['coffee', 'tea'])
         self.assertInHTML('<option value="tea" selected>Tea</option>', html)
         self.assertInHTML('<option value="tea" selected>Tea</option>', html)
         self.assertInHTML('<option value="coffee" selected>Coffee</option>', html)
         self.assertInHTML('<option value="coffee" selected>Coffee</option>', html)
         self.assertTrue(html.count('selected'), 2)
         self.assertTrue(html.count('selected'), 2)
-

+ 2 - 1
coderedcms/fields.py

@@ -2,7 +2,8 @@ from django.db import models
 
 
 from coderedcms.widgets import ColorPickerWidget
 from coderedcms.widgets import ColorPickerWidget
 
 
-class ColorField(models.CharField):    
+
+class ColorField(models.CharField):
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         kwargs['max_length'] = 255
         kwargs['max_length'] = 255
         super(ColorField, self).__init__(*args, **kwargs)
         super(ColorField, self).__init__(*args, **kwargs)

+ 17 - 7
coderedcms/forms.py

@@ -65,12 +65,12 @@ class SecureFileField(forms.FileField):
 
 
     def _check_whitelist(self, value):
     def _check_whitelist(self, value):
         if cr_settings['PROTECTED_MEDIA_UPLOAD_WHITELIST']:
         if cr_settings['PROTECTED_MEDIA_UPLOAD_WHITELIST']:
-            if os.path.splitext(value.name)[1].lower() not in cr_settings['PROTECTED_MEDIA_UPLOAD_WHITELIST']:
+            if os.path.splitext(value.name)[1].lower() not in cr_settings['PROTECTED_MEDIA_UPLOAD_WHITELIST']:  # noqa
                 raise ValidationError(self.error_messages['whitelist_file'])
                 raise ValidationError(self.error_messages['whitelist_file'])
 
 
     def _check_blacklist(self, value):
     def _check_blacklist(self, value):
         if cr_settings['PROTECTED_MEDIA_UPLOAD_BLACKLIST']:
         if cr_settings['PROTECTED_MEDIA_UPLOAD_BLACKLIST']:
-            if os.path.splitext(value.name)[1].lower() in cr_settings['PROTECTED_MEDIA_UPLOAD_BLACKLIST']:
+            if os.path.splitext(value.name)[1].lower() in cr_settings['PROTECTED_MEDIA_UPLOAD_BLACKLIST']:  # noqa
                 raise ValidationError(self.error_messages['blacklist_file'])
                 raise ValidationError(self.error_messages['blacklist_file'])
 
 
 
 
@@ -79,6 +79,7 @@ class SecureFileField(forms.FileField):
 class CoderedDateInput(forms.DateInput):
 class CoderedDateInput(forms.DateInput):
     template_name = 'coderedcms/formfields/date.html'
     template_name = 'coderedcms/formfields/date.html'
 
 
+
 class CoderedDateField(forms.DateField):
 class CoderedDateField(forms.DateField):
     widget = CoderedDateInput()
     widget = CoderedDateInput()
 
 
@@ -88,6 +89,7 @@ class CoderedDateField(forms.DateField):
 class CoderedDateTimeInput(forms.DateTimeInput):
 class CoderedDateTimeInput(forms.DateTimeInput):
     template_name = 'coderedcms/formfields/datetime.html'
     template_name = 'coderedcms/formfields/datetime.html'
 
 
+
 class CoderedDateTimeField(forms.DateTimeField):
 class CoderedDateTimeField(forms.DateTimeField):
     widget = CoderedDateTimeInput()
     widget = CoderedDateTimeInput()
     input_formats = ['%Y-%m-%dT%H:%M', '%m/%d/%Y %I:%M %p', '%m/%d/%Y %I:%M%p', '%m/%d/%Y %H:%M']
     input_formats = ['%Y-%m-%dT%H:%M', '%m/%d/%Y %I:%M %p', '%m/%d/%Y %I:%M%p', '%m/%d/%Y %H:%M']
@@ -98,6 +100,7 @@ class CoderedDateTimeField(forms.DateTimeField):
 class CoderedTimeInput(forms.TimeInput):
 class CoderedTimeInput(forms.TimeInput):
     template_name = 'coderedcms/formfields/time.html'
     template_name = 'coderedcms/formfields/time.html'
 
 
+
 class CoderedTimeField(forms.TimeField):
 class CoderedTimeField(forms.TimeField):
     widget = CoderedTimeInput()
     widget = CoderedTimeInput()
     input_formats = ['%H:%M', '%I:%M %p', '%I:%M%p']
     input_formats = ['%H:%M', '%I:%M %p', '%I:%M%p']
@@ -122,8 +125,6 @@ class CoderedFormBuilder(FormBuilder):
 
 
 
 
 class CoderedSubmissionsListView(WagtailSubmissionsListView):
 class CoderedSubmissionsListView(WagtailSubmissionsListView):
-
-    
     def get_csv_response(self, context):
     def get_csv_response(self, context):
         filename = self.get_csv_filename()
         filename = self.get_csv_filename()
         response = HttpResponse(content_type='text/csv; charset=utf-8')
         response = HttpResponse(content_type='text/csv; charset=utf-8')
@@ -145,7 +146,7 @@ class CoderedFormField(AbstractFormField):
     class Meta:
     class Meta:
         abstract = True
         abstract = True
 
 
-    field_type = models.CharField(verbose_name=_('field type'), max_length=16, choices=FORM_FIELD_CHOICES, blank=True)
+    field_type = models.CharField(verbose_name=_('field type'), max_length=16, choices=FORM_FIELD_CHOICES, blank=True)  # noqa
 
 
 
 
 class SearchForm(forms.Form):
 class SearchForm(forms.Form):
@@ -161,11 +162,20 @@ class SearchForm(forms.Form):
         label=_('Page type'),
         label=_('Page type'),
     )
     )
 
 
+
 def get_page_model_choices():
 def get_page_model_choices():
     """
     """
-    Returns a list of tuples of all creatable Codered pages in the format of ("Custom Codered Page", "CustomCoderedPage")
+    Returns a list of tuples of all creatable Codered pages
+    in the format of ("Custom Codered Page", "CustomCoderedPage")
     """
     """
     from coderedcms.models import get_page_models
     from coderedcms.models import get_page_models
     return (
     return (
-        (page.__name__, re.sub(r'((?<=[a-z])[A-Z]|(?<!\A)[A-Z](?=[a-z]))', r' \1', page.__name__)) for page in get_page_models() if page.is_creatable
+        (
+            page.__name__,
+            re.sub(
+                r'((?<=[a-z])[A-Z]|(?<!\A)[A-Z](?=[a-z]))',
+                r' \1',
+                page.__name__
+            )
+        ) for page in get_page_models() if page.is_creatable
     )
     )

+ 11 - 14
coderedcms/importexport.py

@@ -36,11 +36,12 @@ def import_pages(import_data, parent_page):
     page_content_type = ContentType.objects.get_for_model(Page)
     page_content_type = ContentType.objects.get_for_model(Page)
 
 
     for page_record in import_data['pages']:
     for page_record in import_data['pages']:
-        # build a base Page instance from the exported content (so that we pick up its title and other
-        # core attributes)
+        # build a base Page instance from the exported content
+        # (so that we pick up its title and other core attributes)
         page = Page.from_serializable_data(page_record['content'])
         page = Page.from_serializable_data(page_record['content'])
 
 
-        # clear id and treebeard-related fields so that they get reassigned when we save via add_child
+        # clear id and treebeard-related fields so that
+        # they get reassigned when we save via add_child
         page.id = None
         page.id = None
         page.path = None
         page.path = None
         page.depth = None
         page.depth = None
@@ -62,7 +63,11 @@ def import_pages(import_data, parent_page):
         # Raises LookupError exception if there is no matching model
         # Raises LookupError exception if there is no matching model
         model = apps.get_model(page_record['app_label'], page_record['model'])
         model = apps.get_model(page_record['app_label'], page_record['model'])
 
 
-        specific_page = model.from_serializable_data(page_record['content'], check_fks=False, strict_fks=False)
+        specific_page = model.from_serializable_data(
+            page_record['content'],
+            check_fks=False,
+            strict_fks=False
+        )
         base_page = pages_by_original_id[specific_page.id]
         base_page = pages_by_original_id[specific_page.id]
         specific_page.page_ptr = base_page
         specific_page.page_ptr = base_page
         specific_page.__dict__.update(base_page.__dict__)
         specific_page.__dict__.update(base_page.__dict__)
@@ -74,16 +79,8 @@ def import_pages(import_data, parent_page):
 
 
 
 
 def convert_csv_to_json(csv_file, page_type):
 def convert_csv_to_json(csv_file, page_type):
-    pages_json = {
-        "pages": []
-    }
-    default_page_data = {
-        "app_label": "website",
-        "content": {
-            "pk": None
-        },
-        "model": page_type
-    }
+    pages_json = {"pages": []}
+    default_page_data = {"app_label": "website", "content": {"pk": None}, "model": page_type}
 
 
     pages_csv_dict = csv.DictReader(csv_file)
     pages_csv_dict = csv.DictReader(csv_file)
     for row in pages_csv_dict:
     for row in pages_csv_dict:

+ 4 - 4
coderedcms/models/__init__.py

@@ -4,7 +4,7 @@ into files based on their purpose, but provide them all via
 a single `models` module.
 a single `models` module.
 """
 """
 
 
-from .integration_models import * #noqa
-from .page_models import * #noqa
-from .snippet_models import * #noqa
-from .wagtailsettings_models import * #noqa
+from .integration_models import *  # noqa
+from .page_models import *  # noqa
+from .snippet_models import *  # noqa
+from .wagtailsettings_models import *  # noqa

+ 20 - 16
coderedcms/models/integration_models.py

@@ -1,13 +1,11 @@
 from django.db import models
 from django.db import models
-from django.forms.widgets import Select, Input
+from django.forms.widgets import Input
 from django.template import Context, Template
 from django.template import Context, Template
 from django.template.loader import render_to_string
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
 from wagtail.admin.edit_handlers import FieldPanel
 from wagtail.admin.edit_handlers import FieldPanel
 from wagtail.core import hooks
 from wagtail.core import hooks
-from wagtail.core.models import Orderable, Page
-from modelcluster.fields import ParentalKey
 
 
 from coderedcms.api.mailchimp import MailchimpApi
 from coderedcms.api.mailchimp import MailchimpApi
 
 
@@ -26,7 +24,8 @@ class MailchimpSubscriberIntegrationWidget(Input):
         list_library = self.build_list_library()
         list_library = self.build_list_library()
         ctx['widget']['value'] = json.dumps(json_value)
         ctx['widget']['value'] = json.dumps(json_value)
         ctx['widget']['extra_js'] = self.render_js(name, list_library, json_value)
         ctx['widget']['extra_js'] = self.render_js(name, list_library, json_value)
-        ctx['widget']['selectable_mailchimp_lists'] = self.get_selectable_mailchimp_lists(list_library)
+        ctx['widget']['selectable_mailchimp_lists'] = self.get_selectable_mailchimp_lists(
+            list_library)
         ctx['widget']['stored_mailchimp_list'] = self.get_stored_mailchimp_list(json_value)
         ctx['widget']['stored_mailchimp_list'] = self.get_stored_mailchimp_list(json_value)
 
 
         return ctx
         return ctx
@@ -34,15 +33,14 @@ class MailchimpSubscriberIntegrationWidget(Input):
     def render_js(self, name, list_library, json_value):
     def render_js(self, name, list_library, json_value):
         ctx = {
         ctx = {
             'widget_name': name,
             'widget_name': name,
-            'widget_js_name' : name.replace('-', '_'),
-            'list_library' : list_library,
+            'widget_js_name': name.replace('-', '_'),
+            'list_library': list_library,
             'stored_mailchimp_list': self.get_stored_mailchimp_list(json_value),
             'stored_mailchimp_list': self.get_stored_mailchimp_list(json_value),
-            'stored_merge_fields' : self.get_stored_merge_fields(json_value),
+            'stored_merge_fields': self.get_stored_merge_fields(json_value),
         }
         }
 
 
         return render_to_string(self.js_template_name, ctx)
         return render_to_string(self.js_template_name, ctx)
 
 
-
     def get_json_value(self, value):
     def get_json_value(self, value):
         if value:
         if value:
             json_value = json.loads(value)
             json_value = json.loads(value)
@@ -85,18 +83,22 @@ class MailchimpSubscriberIntegrationWidget(Input):
                     'merge_fields': {},
                     'merge_fields': {},
                     'interest_categories': {}
                     'interest_categories': {}
                 }
                 }
-                
-                list_library[l['id']]['merge_fields'] = mailchimp.get_merge_fields_for_list(l['id'])['merge_fields']
-                list_library[l['id']]['interest_categories'] = mailchimp.get_interest_categories_for_list(l['id'])['categories']
+
+                list_library[l['id']]['merge_fields'] = mailchimp.get_merge_fields_for_list(l['id'])['merge_fields']  # noqa
+                list_library[l['id']]['interest_categories'] = mailchimp.get_interest_categories_for_list(l['id'])['categories']  # noqa
 
 
                 for category in list_library[l['id']]['interest_categories']:
                 for category in list_library[l['id']]['interest_categories']:
-                    category['interests'] = mailchimp.get_interests_for_interest_category(l['id'], category['id'])['interests']
+                    category['interests'] = mailchimp.get_interests_for_interest_category(
+                        l['id'],
+                        category['id']
+                    )['interests']
 
 
         return list_library
         return list_library
 
 
+
 class MailchimpSubscriberIntegration(models.Model):
 class MailchimpSubscriberIntegration(models.Model):
     class Meta:
     class Meta:
-        abstract=True
+        abstract = True
 
 
     subscriber_json_data = models.TextField(
     subscriber_json_data = models.TextField(
         blank=True,
         blank=True,
@@ -106,7 +108,8 @@ class MailchimpSubscriberIntegration(models.Model):
     def integration_operation(self, instance, **kwargs):
     def integration_operation(self, instance, **kwargs):
         mailchimp = MailchimpApi()
         mailchimp = MailchimpApi()
         if mailchimp.is_active:
         if mailchimp.is_active:
-            rendered_dictionary = self.render_dictionary(self.format_form_submission(kwargs['form_submission']))
+            rendered_dictionary = self.render_dictionary(
+                self.format_form_submission(kwargs['form_submission']))
             mailchimp.add_user_to_list(list_id=self.get_list_id(), data=rendered_dictionary)
             mailchimp.add_user_to_list(list_id=self.get_list_id(), data=rendered_dictionary)
 
 
     def format_form_submission(self, form_submission):
     def format_form_submission(self, form_submission):
@@ -146,14 +149,15 @@ class MailchimpSubscriberIntegration(models.Model):
             ]
             ]
         })
         })
 
 
-
-        rendered_dictionary = Template(rendered_dictionary_template).render(Context(form_submission))
+        rendered_dictionary = Template(
+            rendered_dictionary_template).render(Context(form_submission))
         return rendered_dictionary
         return rendered_dictionary
 
 
     panels = [
     panels = [
         FieldPanel('subscriber_json_data', widget=MailchimpSubscriberIntegrationWidget)
         FieldPanel('subscriber_json_data', widget=MailchimpSubscriberIntegrationWidget)
     ]
     ]
 
 
+
 @hooks.register('form_page_submit')
 @hooks.register('form_page_submit')
 def run_mailchimp_subscriber_integrations(instance, **kwargs):
 def run_mailchimp_subscriber_integrations(instance, **kwargs):
     if hasattr(instance, 'integration_panels'):
     if hasattr(instance, 'integration_panels'):

+ 114 - 73
coderedcms/models/page_models.py

@@ -12,7 +12,7 @@ from django.contrib import messages
 from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
 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.mail import EmailMessage
 from django.core.mail import EmailMessage
-from django.core.paginator import Paginator
+from django.core.paginator import Paginator, InvalidPage, EmptyPage, 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, MinValueValidator
 from django.db import models
 from django.db import models
@@ -41,7 +41,8 @@ from wagtail.admin.edit_handlers import (
     ObjectList,
     ObjectList,
     PageChooserPanel,
     PageChooserPanel,
     StreamFieldPanel,
     StreamFieldPanel,
-    TabbedInterface)
+    TabbedInterface
+)
 from wagtail.core import hooks
 from wagtail.core import hooks
 from wagtail.core.fields import StreamField
 from wagtail.core.fields import StreamField
 from wagtail.core.models import Orderable, PageBase, Page, Site
 from wagtail.core.models import Orderable, PageBase, Page, Site
@@ -60,13 +61,21 @@ from coderedcms.blocks import (
     STREAMFORM_BLOCKS,
     STREAMFORM_BLOCKS,
     ContentWallBlock,
     ContentWallBlock,
     OpenHoursBlock,
     OpenHoursBlock,
-    StructuredDataActionBlock)
+    StructuredDataActionBlock
+)
 from coderedcms.fields import ColorField
 from coderedcms.fields import ColorField
 from coderedcms.forms import CoderedFormBuilder, CoderedSubmissionsListView
 from coderedcms.forms import CoderedFormBuilder, CoderedSubmissionsListView
 from coderedcms.models.snippet_models import ClassifierTerm
 from coderedcms.models.snippet_models import ClassifierTerm
-from coderedcms.models.wagtailsettings_models import GeneralSettings, LayoutSettings, SeoSettings, GoogleApiSettings
+from coderedcms.models.wagtailsettings_models import GeneralSettings, LayoutSettings, SeoSettings, GoogleApiSettings  # noqa
 from coderedcms.wagtail_flexible_forms.blocks import FormFieldBlock, FormStepBlock
 from coderedcms.wagtail_flexible_forms.blocks import FormFieldBlock, FormStepBlock
-from coderedcms.wagtail_flexible_forms.models import Step, Steps, StreamFormMixin, StreamFormJSONEncoder, SessionFormSubmission, SubmissionRevision
+from coderedcms.wagtail_flexible_forms.models import (
+    Step,
+    Steps,
+    StreamFormMixin,
+    StreamFormJSONEncoder,
+    SessionFormSubmission,
+    SubmissionRevision
+)
 from coderedcms.settings import cr_settings
 from coderedcms.settings import cr_settings
 from coderedcms.widgets import ClassifierSelectWidget
 from coderedcms.widgets import ClassifierSelectWidget
 
 
@@ -76,6 +85,7 @@ logger = logging.getLogger('coderedcms')
 
 
 CODERED_PAGE_MODELS = []
 CODERED_PAGE_MODELS = []
 
 
+
 def get_page_models():
 def get_page_models():
     return CODERED_PAGE_MODELS
     return CODERED_PAGE_MODELS
 
 
@@ -100,11 +110,13 @@ class CoderedPageMeta(PageBase):
         if not cls._meta.abstract:
         if not cls._meta.abstract:
             CODERED_PAGE_MODELS.append(cls)
             CODERED_PAGE_MODELS.append(cls)
 
 
+
 class CoderedTag(TaggedItemBase):
 class CoderedTag(TaggedItemBase):
     class Meta:
     class Meta:
         verbose_name = _('CodeRed Tag')
         verbose_name = _('CodeRed Tag')
     content_object = ParentalKey('coderedcms.CoderedPage', related_name='tagged_items')
     content_object = ParentalKey('coderedcms.CoderedPage', related_name='tagged_items')
 
 
+
 class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
 class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
     """
     """
     General use page with caching, templating, and SEO functionality.
     General use page with caching, templating, and SEO functionality.
@@ -124,7 +136,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
     # ajax_template = ''
     # ajax_template = ''
     # search_template = ''
     # search_template = ''
 
 
-
     ###############
     ###############
     # Content fields
     # Content fields
     ###############
     ###############
@@ -138,7 +149,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         verbose_name=_('Cover image'),
         verbose_name=_('Cover image'),
     )
     )
 
 
-
     ###############
     ###############
     # Index fields
     # Index fields
     ###############
     ###############
@@ -184,7 +194,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         help_text=_('Enable filtering child pages by these classifiers.'),
         help_text=_('Enable filtering child pages by these classifiers.'),
     )
     )
 
 
-
     ###############
     ###############
     # Layout fields
     # Layout fields
     ###############
     ###############
@@ -196,7 +205,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         verbose_name=_('Template')
         verbose_name=_('Template')
     )
     )
 
 
-
     ###############
     ###############
     # SEO fields
     # SEO fields
     ###############
     ###############
@@ -208,7 +216,9 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         on_delete=models.SET_NULL,
         on_delete=models.SET_NULL,
         related_name='+',
         related_name='+',
         verbose_name=_('Open Graph preview image'),
         verbose_name=_('Open Graph preview image'),
-        help_text=_('The image shown when linking to this page on social media. If blank, defaults to article cover image, or logo in Settings > Layout > Logo')
+        help_text=_(
+            "The image shown when linking to this page on social media. If blank, defaults to article cover image, or logo in Settings > Layout > Logo"  # noqa
+        )
     )
     )
     struct_org_type = models.CharField(
     struct_org_type = models.CharField(
         default='',
         default='',
@@ -241,7 +251,9 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         on_delete=models.SET_NULL,
         on_delete=models.SET_NULL,
         related_name='+',
         related_name='+',
         verbose_name=_('Photo of Organization'),
         verbose_name=_('Photo of Organization'),
-        help_text=_('A photo of the facility. This photo will be cropped to 1:1, 4:3, and 16:9 aspect ratios automatically.')
+        help_text=_(
+            "A photo of the facility. This photo will be cropped to 1:1, 4:3, and 16:9 aspect ratios automatically."  # noqa
+        )
     )
     )
     struct_org_phone = models.CharField(
     struct_org_phone = models.CharField(
         blank=True,
         blank=True,
@@ -277,7 +289,9 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         blank=True,
         blank=True,
         max_length=255,
         max_length=255,
         verbose_name=_('Country'),
         verbose_name=_('Country'),
-        help_text=_('For example, USA. Two-letter ISO 3166-1 alpha-2 country code is also acceptible https://en.wikipedia.org/wiki/ISO_3166-1')
+        help_text=_(
+            "For example, USA. Two-letter ISO 3166-1 alpha-2 country code is also acceptible https://en.wikipedia.org/wiki/ISO_3166-1"  # noqa
+        )
     )
     )
     struct_org_geo_lat = models.DecimalField(
     struct_org_geo_lat = models.DecimalField(
         blank=True,
         blank=True,
@@ -310,10 +324,11 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
     struct_org_extra_json = models.TextField(
     struct_org_extra_json = models.TextField(
         blank=True,
         blank=True,
         verbose_name=_('Additional Organization markup'),
         verbose_name=_('Additional Organization markup'),
-        help_text=_('Additional JSON-LD inserted into the Organization dictionary. Must be properties of https://schema.org/Organization or the selected organization type.')
+        help_text=_(
+            "Additional JSON-LD inserted into the Organization dictionary. Must be properties of https://schema.org/Organization or the selected organization type."  # noqa
+        )
     )
     )
 
 
-
     ###############
     ###############
     # Classify
     # Classify
     ###############
     ###############
@@ -322,7 +337,9 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         'coderedcms.ClassifierTerm',
         'coderedcms.ClassifierTerm',
         blank=True,
         blank=True,
         verbose_name=_('Classifiers'),
         verbose_name=_('Classifiers'),
-        help_text=_('Categorize and group pages together with classifiers. Used to organize and filter pages across the site.'),
+        help_text=_(
+            "Categorize and group pages together with classifiers. Used to organize and filter pages across the site."  # noqa
+        ),
     )
     )
     tags = ClusterTaggableManager(
     tags = ClusterTaggableManager(
         through=CoderedTag,
         through=CoderedTag,
@@ -331,7 +348,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         help_text=_('Used to organize pages across the site.'),
         help_text=_('Used to organize pages across the site.'),
     )
     )
 
 
-
     ###############
     ###############
     # Settings
     # Settings
     ###############
     ###############
@@ -344,7 +360,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         verbose_name=_('Content Walls')
         verbose_name=_('Content Walls')
     )
     )
 
 
-
     ###############
     ###############
     # Search
     # Search
     ###############
     ###############
@@ -370,7 +385,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         index.FilterField('classifier_terms'),
         index.FilterField('classifier_terms'),
     ]
     ]
 
 
-
     ###############
     ###############
     # Panels
     # Panels
     ###############
     ###############
@@ -460,8 +474,7 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
         klassname = self.__class__.__name__.lower()
         klassname = self.__class__.__name__.lower()
         template_choices = cr_settings['FRONTEND_TEMPLATES_PAGES'].get('*', ()) + \
         template_choices = cr_settings['FRONTEND_TEMPLATES_PAGES'].get('*', ()) + \
-                           cr_settings['FRONTEND_TEMPLATES_PAGES'].get(klassname, ())
-
+            cr_settings['FRONTEND_TEMPLATES_PAGES'].get(klassname, ())
 
 
         self._meta.get_field('index_order_by').choices = self.index_order_by_choices
         self._meta.get_field('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
@@ -469,14 +482,16 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
             self.index_order_by = self.index_order_by_default
             self.index_order_by = self.index_order_by_default
             self.index_show_subpages = self.index_show_subpages_default
             self.index_show_subpages = self.index_show_subpages_default
 
 
-
     @classmethod
     @classmethod
     def get_edit_handler(cls):
     def get_edit_handler(cls):
         """
         """
         Override to "lazy load" the panels overriden by subclasses.
         Override to "lazy load" the panels overriden by subclasses.
         """
         """
         panels = [
         panels = [
-            ObjectList(cls.content_panels + cls.body_content_panels + cls.bottom_content_panels, heading=_('Content')),
+            ObjectList(
+                cls.content_panels + cls.body_content_panels + cls.bottom_content_panels,
+                heading=_('Content')
+            ),
             ObjectList(cls.classify_panels, heading=_('Classify')),
             ObjectList(cls.classify_panels, heading=_('Classify')),
             ObjectList(cls.layout_panels, heading=_('Layout')),
             ObjectList(cls.layout_panels, heading=_('Layout')),
             ObjectList(cls.promote_panels, heading=_('SEO'), classname="seo"),
             ObjectList(cls.promote_panels, heading=_('SEO'), classname="seo"),
@@ -484,7 +499,11 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         ]
         ]
 
 
         if cls.integration_panels:
         if cls.integration_panels:
-            panels.append(ObjectList(cls.integration_panels, heading='Integrations', classname='integrations'))
+            panels.append(ObjectList(
+                cls.integration_panels,
+                heading='Integrations',
+                classname='integrations'
+            ))
 
 
         return TabbedInterface(panels).bind_to_model(cls)
         return TabbedInterface(panels).bind_to_model(cls)
 
 
@@ -580,13 +599,18 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
                         try:
                         try:
                             for term in selected_terms:
                             for term in selected_terms:
                                 all_children = all_children.filter(classifier_terms=term)
                                 all_children = all_children.filter(classifier_terms=term)
-                        except:
-                            logger.warning("Tried to filter by ClassifierTerm, but <%s.%s ('%s')>.get_index_children() did not return a queryset or is not a queryset of CoderedPage models.", self._meta.app_label, self.__class__.__name__, self.title)
+                        except AttributeError:
+                            logger.warning(
+                                "Tried to filter by ClassifierTerm, but <%s.%s ('%s')>.get_index_children() did not return a queryset or is not a queryset of CoderedPage models.",  # noqa
+                                self._meta.app_label,
+                                self.__class__.__name__,
+                                self.title
+                            )
             paginator = Paginator(all_children, self.index_num_per_page)
             paginator = Paginator(all_children, self.index_num_per_page)
             pagenum = request.GET.get('p', 1)
             pagenum = request.GET.get('p', 1)
             try:
             try:
                 paged_children = paginator.page(pagenum)
                 paged_children = paginator.page(pagenum)
-            except:
+            except (PageNotAnInteger, EmptyPage, InvalidPage) as e:  # noqa
                 paged_children = paginator.page(1)
                 paged_children = paginator.page(1)
 
 
             context['index_paginated'] = paged_children
             context['index_paginated'] = paged_children
@@ -594,9 +618,6 @@ class CoderedPage(WagtailCacheMixin, Page, metaclass=CoderedPageMeta):
         context['content_walls'] = self.get_content_walls(check_child_setting=False)
         context['content_walls'] = self.get_content_walls(check_child_setting=False)
         return context
         return context
 
 
-
-
-
 ###############################################################################
 ###############################################################################
 # Abstract pages providing pre-built common website functionality, suitable for subclassing.
 # Abstract pages providing pre-built common website functionality, suitable for subclassing.
 # These are abstract so subclasses can override fields if desired.
 # These are abstract so subclasses can override fields if desired.
@@ -841,10 +862,11 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
             for occurrence in self.occurrences.all():
             for occurrence in self.occurrences.all():
                 aoc += [instance for instance in occurrence.all_occurrences()]
                 aoc += [instance for instance in occurrence.all_occurrences()]
             if len(aoc) > 0:
             if len(aoc) > 0:
-                return aoc[-1] # last one in the list
+                return aoc[-1]  # last one in the list
 
 
         except AttributeError:
         except AttributeError:
-            # Triggers when a preview is initiated on an EventPage because it uses a FakeQuerySet object.
+            # Triggers when a preview is initiated on an
+            # EventPage because it uses a FakeQuerySet object.
             # Here we manually compute the next_occurrence
             # Here we manually compute the next_occurrence
             occurrences = [e.next_occurrence() for e in self.occurrences.all()]
             occurrences = [e.next_occurrence() for e in self.occurrences.all()]
             if occurrences:
             if occurrences:
@@ -862,21 +884,23 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
         }
         }
 
 
         if 'limit' in kwargs:
         if 'limit' in kwargs:
-            if kwargs['limit'] != None:
-                # Limit the number of event instances that will be generated per occurrence rule to 10, if not otherwise specified.
+            if kwargs['limit'] is not None:
+                # Limit the number of event instances that will be
+                # generated per occurrence rule to 10, if not otherwise specified.
                 occurrence_kwargs['limit'] = kwargs.get('limit', 10)
                 occurrence_kwargs['limit'] = kwargs.get('limit', 10)
 
 
         # For each occurrence rule in all of the occurrence rules for this event.
         # For each occurrence rule in all of the occurrence rules for this event.
         for occurrence in self.occurrences.all():
         for occurrence in self.occurrences.all():
 
 
             # Add the qualifying generated event instances to the list.
             # Add the qualifying generated event instances to the list.
-            event_instances += [instance for instance in occurrence.all_occurrences(**occurrence_kwargs)]
+            event_instances += [
+                instance for instance in occurrence.all_occurrences(**occurrence_kwargs)]
 
 
         # Sort all the events by the date that they start
         # Sort all the events by the date that they start
         event_instances.sort(key=lambda d: d[0])
         event_instances.sort(key=lambda d: d[0])
 
 
         # Return the event instances, possibly spliced if num_instances_to_return is set.
         # Return the event instances, possibly spliced if num_instances_to_return is set.
-        return event_instances[:num_of_instances_to_return] if num_of_instances_to_return else event_instances
+        return event_instances[:num_of_instances_to_return] if num_of_instances_to_return else event_instances  # noqa
 
 
     def convert_to_ical_format(self, dt_start=None, dt_end=None, occurrence=None):
     def convert_to_ical_format(self, dt_start=None, dt_end=None, occurrence=None):
         ical_event = ICalEvent()
         ical_event = ICalEvent()
@@ -892,7 +916,8 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
 
 
         if occurrence:
         if occurrence:
             freq = occurrence.repeat.split(":")[1] if occurrence.repeat else None
             freq = occurrence.repeat.split(":")[1] if occurrence.repeat else None
-            repeat_until = occurrence.repeat_until.strftime("%Y%m%dT000000Z") if occurrence.repeat_until else None
+            repeat_until = occurrence.repeat_until.strftime(
+                "%Y%m%dT000000Z") if occurrence.repeat_until else None
 
 
             ical_event.add('dtstart', occurrence.start)
             ical_event.add('dtstart', occurrence.start)
 
 
@@ -916,6 +941,7 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
             events.append(self.convert_to_ical_format(occurrence=occurrence))
             events.append(self.convert_to_ical_format(occurrence=occurrence))
         return events
         return events
 
 
+
 class DefaultCalendarViewChoices():
 class DefaultCalendarViewChoices():
     MONTH = 'month'
     MONTH = 'month'
     AGENDA_WEEK = 'agendaWeek'
     AGENDA_WEEK = 'agendaWeek'
@@ -930,6 +956,7 @@ class DefaultCalendarViewChoices():
         (LIST_MONTH, _('Calendar List View')),
         (LIST_MONTH, _('Calendar List View')),
     )
     )
 
 
+
 class CoderedEventIndexPage(CoderedWebPage):
 class CoderedEventIndexPage(CoderedWebPage):
     """
     """
     Shows a list of event sub-pages.
     Shows a list of event sub-pages.
@@ -984,7 +1011,7 @@ class CoderedEventIndexPage(CoderedWebPage):
                 event_data = {
                 event_data = {
                     'title': event.title,
                     'title': event.title,
                     'start': occurrence[0].strftime('%Y-%m-%dT%H:%M:%S'),
                     'start': occurrence[0].strftime('%Y-%m-%dT%H:%M:%S'),
-                    'end' : occurrence[1].strftime('%Y-%m-%dT%H:%M:%S') if occurrence[1] else "",
+                    'end': occurrence[1].strftime('%Y-%m-%dT%H:%M:%S') if occurrence[1] else "",
                     'description': "",
                     'description': "",
                 }
                 }
                 if event.url:
                 if event.url:
@@ -1002,25 +1029,28 @@ class CoderedEventOccurrence(Orderable, BaseOccurrence):
 
 
 
 
 class CoderedFormMixin(models.Model):
 class CoderedFormMixin(models.Model):
-
     class Meta:
     class Meta:
         abstract = True
         abstract = True
 
 
     submissions_list_view_class = CoderedSubmissionsListView
     submissions_list_view_class = CoderedSubmissionsListView
     encoder = DjangoJSONEncoder
     encoder = DjangoJSONEncoder
 
 
-    ### Custom codered fields
+    # Custom codered fields
     to_address = models.CharField(
     to_address = models.CharField(
         max_length=255,
         max_length=255,
         blank=True,
         blank=True,
         verbose_name=_('Email form submissions to'),
         verbose_name=_('Email form submissions to'),
-        help_text=_('Optional - email form submissions to this address. Separate multiple addresses by comma.')
+        help_text=_(
+            "Optional - email form submissions to this address. Separate multiple addresses by comma."  # noqa
+        )
     )
     )
     reply_address = models.CharField(
     reply_address = models.CharField(
         max_length=255,
         max_length=255,
         blank=True,
         blank=True,
         verbose_name=_('Reply-to address'),
         verbose_name=_('Reply-to address'),
-        help_text=_('Optional - to reply to the submitter, specify the email field here. For example, if a form field above is labeled "Your Email", enter: {{ your_email }}')
+        help_text=_(
+            "Optional - to reply to the submitter, specify the email field here. For example, if a form field above is labeled 'Your Email', enter: {{ your_email }}"  # noqa
+        )
     )
     )
     subject = models.CharField(
     subject = models.CharField(
         max_length=255,
         max_length=255,
@@ -1136,7 +1166,6 @@ class CoderedFormMixin(models.Model):
         FieldPanel('spam_protection')
         FieldPanel('spam_protection')
     ]
     ]
 
 
-
     @property
     @property
     def form_live(self):
     def form_live(self):
         """
         """
@@ -1174,9 +1203,9 @@ class CoderedFormMixin(models.Model):
 
 
     def get_storage(self):
     def get_storage(self):
         return FileSystemStorage(
         return FileSystemStorage(
-                location=cr_settings['PROTECTED_MEDIA_ROOT'],
-                base_url=cr_settings['PROTECTED_MEDIA_URL']
-            )
+            location=cr_settings['PROTECTED_MEDIA_ROOT'],
+            base_url=cr_settings['PROTECTED_MEDIA_URL']
+        )
 
 
     def process_form_submission(self, request, form, form_submission, processed_data):
     def process_form_submission(self, request, form, form_submission, processed_data):
 
 
@@ -1412,13 +1441,13 @@ class CoderedFormPage(CoderedFormMixin, CoderedWebPage):
     form_builder = CoderedFormBuilder
     form_builder = CoderedFormBuilder
 
 
     body_content_panels = [
     body_content_panels = [
-            InlinePanel('form_fields', label="Form fields"),
-        ] + \
+        InlinePanel('form_fields', label="Form fields"),
+    ] + \
         CoderedWebPage.body_content_panels + \
         CoderedWebPage.body_content_panels + \
         CoderedFormMixin.body_content_panels + [
         CoderedFormMixin.body_content_panels + [
             FormSubmissionsPanel(),
             FormSubmissionsPanel(),
             InlinePanel('confirmation_emails', label=_('Confirmation Emails'))
             InlinePanel('confirmation_emails', label=_('Confirmation Emails'))
-        ]
+    ]
 
 
     settings_panels = CoderedPage.settings_panels + CoderedFormMixin.settings_panels
     settings_panels = CoderedPage.settings_panels + CoderedFormMixin.settings_panels
 
 
@@ -1494,9 +1523,9 @@ class CoderedSessionFormSubmission(SessionFormSubmission):
         if 'user' in submission_data:
         if 'user' in submission_data:
             submission_data['user'] = str(submission_data['user'])
             submission_data['user'] = str(submission_data['user'])
         submission = FormSubmission.objects.create(
         submission = FormSubmission.objects.create(
-                form_data=json.dumps(submission_data, cls=StreamFormJSONEncoder),
-                page=self.page
-            )
+            form_data=json.dumps(submission_data, cls=StreamFormJSONEncoder),
+            page=self.page
+        )
 
 
         if delete_self:
         if delete_self:
             CoderedSubmissionRevision.objects.filter(submission_id=self.id).delete()
             CoderedSubmissionRevision.objects.filter(submission_id=self.id).delete()
@@ -1510,11 +1539,9 @@ class CoderedSessionFormSubmission(SessionFormSubmission):
     def render_link(self, value):
     def render_link(self, value):
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
 
 
-
     def render_image(self, value):
     def render_image(self, value):
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
 
 
-
     def render_file(self, value):
     def render_file(self, value):
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
         return "{0}{1}".format(cr_settings['PROTECTED_MEDIA_URL'], value)
 
 
@@ -1526,8 +1553,7 @@ def create_submission_changed_revision(sender, **kwargs):
     submission = kwargs['instance']
     submission = kwargs['instance']
     created = kwargs['created']
     created = kwargs['created']
     CoderedSubmissionRevision.create_from_submission(
     CoderedSubmissionRevision.create_from_submission(
-        submission, (CoderedSubmissionRevision.CREATED if created
-                     else CoderedSubmissionRevision.CHANGED))
+        submission, (CoderedSubmissionRevision.CREATED if created else CoderedSubmissionRevision.CHANGED))  # noqa
 
 
 
 
 @receiver(post_delete)
 @receiver(post_delete)
@@ -1535,8 +1561,7 @@ def create_submission_deleted_revision(sender, **kwargs):
     if not issubclass(sender, CoderedSessionFormSubmission):
     if not issubclass(sender, CoderedSessionFormSubmission):
         return
         return
     submission = kwargs['instance']
     submission = kwargs['instance']
-    CoderedSubmissionRevision.create_from_submission(submission,
-                                              SubmissionRevision.DELETED)
+    CoderedSubmissionRevision.create_from_submission(submission, SubmissionRevision.DELETED)  # noqa
 
 
 
 
 class CoderedStep(Step):
 class CoderedStep(Step):
@@ -1571,7 +1596,7 @@ class CoderedSteps(Steps):
 
 
 class CoderedStreamFormMixin(StreamFormMixin):
 class CoderedStreamFormMixin(StreamFormMixin):
     class Meta:
     class Meta:
-        abstract=True
+        abstract = True
 
 
     def get_steps(self, request=None):
     def get_steps(self, request=None):
         if not hasattr(self, 'steps'):
         if not hasattr(self, 'steps'):
@@ -1625,8 +1650,8 @@ class CoderedStreamFormPage(CoderedFormMixin, CoderedStreamFormMixin, CoderedWeb
     body_content_panels = [
     body_content_panels = [
         StreamFieldPanel('form_fields')
         StreamFieldPanel('form_fields')
     ] + \
     ] + \
-    CoderedFormMixin.body_content_panels + [
-        InlinePanel('confirmation_emails', label=_('Confirmation Emails'))
+        CoderedFormMixin.body_content_panels + [
+            InlinePanel('confirmation_emails', label=_('Confirmation Emails'))
     ]
     ]
 
 
     def process_form_post(self, form, request):
     def process_form_post(self, form, request):
@@ -1653,9 +1678,10 @@ class CoderedStreamFormPage(CoderedFormMixin, CoderedStreamFormMixin, CoderedWeb
 
 
     def get_storage(self):
     def get_storage(self):
         return FileSystemStorage(
         return FileSystemStorage(
-                location=cr_settings['PROTECTED_MEDIA_ROOT'],
-                base_url=cr_settings['PROTECTED_MEDIA_URL']
-            )
+            location=cr_settings['PROTECTED_MEDIA_ROOT'],
+            base_url=cr_settings['PROTECTED_MEDIA_URL']
+        )
+
 
 
 class CoderedLocationPage(CoderedWebPage):
 class CoderedLocationPage(CoderedWebPage):
     """
     """
@@ -1687,7 +1713,8 @@ class CoderedLocationPage(CoderedWebPage):
     auto_update_latlng = models.BooleanField(
     auto_update_latlng = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_("Auto Update Latitude and Longitude"),
         verbose_name=_("Auto Update Latitude and Longitude"),
-        help_text=_("If checked, automatically update the latitude and longitude when the address is updated.")
+        help_text=_(
+            "If checked, automatically update the latitude and longitude when the address is updated.")  # noqa
     )
     )
     map_title = models.CharField(
     map_title = models.CharField(
         blank=True,
         blank=True,
@@ -1767,20 +1794,24 @@ class CoderedLocationPage(CoderedWebPage):
     def to_geojson(self):
     def to_geojson(self):
         return {
         return {
             "type": "Feature",
             "type": "Feature",
-            "geometry":{
+            "geometry": {
                 "type": "Point",
                 "type": "Point",
                 "coordinates": [self.longitude, self.latitude]
                 "coordinates": [self.longitude, self.latitude]
             },
             },
-            "properties":{
+            "properties": {
                 "list_description": self.render_list_description,
                 "list_description": self.render_list_description,
                 "pin_description": self.render_pin_description
                 "pin_description": self.render_pin_description
             }
             }
         }
         }
 
 
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
-        if self.auto_update_latlng and GoogleApiSettings.for_site(Site.objects.get(is_default_site=True)).google_maps_api_key:
+        if self.auto_update_latlng and GoogleApiSettings.for_site(
+            Site.objects.get(is_default_site=True)
+        ).google_maps_api_key:
             try:
             try:
-                g = geocoder.google(self.address, key=GoogleApiSettings.for_site(Site.objects.get(is_default_site=True)).google_maps_api_key)
+                g = geocoder.google(self.address, key=GoogleApiSettings.for_site(
+                    Site.objects.get(is_default_site=True)
+                ).google_maps_api_key)
                 self.latitude = g.latlng[0]
                 self.latitude = g.latlng[0]
                 self.longitude = g.latlng[1]
                 self.longitude = g.latlng[1]
             except TypeError:
             except TypeError:
@@ -1789,10 +1820,11 @@ class CoderedLocationPage(CoderedWebPage):
 
 
         return super(CoderedLocationPage, self).save(*args, **kwargs)
         return super(CoderedLocationPage, self).save(*args, **kwargs)
 
 
-
     def get_context(self, request, *args, **kwargs):
     def get_context(self, request, *args, **kwargs):
         context = super().get_context(request)
         context = super().get_context(request)
-        context['google_api_key'] = GoogleApiSettings.for_site(Site.objects.get(is_default_site=True)).google_maps_api_key
+        context['google_api_key'] = GoogleApiSettings.for_site(
+            Site.objects.get(is_default_site=True)
+        ).google_maps_api_key
         return context
         return context
 
 
 
 
@@ -1826,7 +1858,9 @@ class CoderedLocationIndexPage(CoderedWebPage):
             MaxValueValidator(20),
             MaxValueValidator(20),
             MinValueValidator(1),
             MinValueValidator(1),
         ],
         ],
-        help_text=_('Requires API key to use zoom. 1: World, 5: Landmass/continent, 10: City, 15: Streets, 20: Buildings')
+        help_text=_(
+            "Requires API key to use zoom. 1: World, 5: Landmass/continent, 10: City, 15: Streets, 20: Buildings"  # noqa
+        )
     )
     )
 
 
     layout_panels = CoderedWebPage.layout_panels + [
     layout_panels = CoderedWebPage.layout_panels + [
@@ -1858,7 +1892,12 @@ class CoderedLocationIndexPage(CoderedWebPage):
             southwest = [float(x) for x in southwest.split(',')]
             southwest = [float(x) for x in southwest.split(',')]
             northeast = [float(x) for x in northeast.split(',')]
             northeast = [float(x) for x in northeast.split(',')]
 
 
-            qs = qs.filter(latitude__gte=southwest[0], latitude__lte=northeast[0], longitude__gte=southwest[1], longitude__lte=northeast[1])
+            qs = qs.filter(
+                latitude__gte=southwest[0],
+                latitude__lte=northeast[0],
+                longitude__gte=southwest[1],
+                longitude__lte=northeast[1]
+            )
 
 
         return {
         return {
             "type": "FeatureCollection",
             "type": "FeatureCollection",
@@ -1879,5 +1918,7 @@ class CoderedLocationIndexPage(CoderedWebPage):
 
 
     def get_context(self, request, *args, **kwargs):
     def get_context(self, request, *args, **kwargs):
         context = super().get_context(request)
         context = super().get_context(request)
-        context['google_api_key'] = GoogleApiSettings.for_site(Site.objects.get(is_default_site=True)).google_maps_api_key
+        context['google_api_key'] = GoogleApiSettings.for_site(
+            Site.objects.get(is_default_site=True)
+        ).google_maps_api_key
         return context
         return context

+ 36 - 7
coderedcms/models/snippet_models.py

@@ -38,12 +38,14 @@ class Carousel(ClusterableModel):
     show_controls = models.BooleanField(
     show_controls = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Show controls'),
         verbose_name=_('Show controls'),
-        help_text=_('Shows arrows on the left and right of the carousel to advance next or previous slides.'),
+        help_text=_(
+            "Shows arrows on the left and right of the carousel to advance next or previous slides."  # noqa
+        ),
     )
     )
     show_indicators = models.BooleanField(
     show_indicators = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Show indicators'),
         verbose_name=_('Show indicators'),
-        help_text=_('Shows small indicators at the bottom of the carousel based on the number of slides.'),
+        help_text=_('Shows small indicators at the bottom of the carousel based on the number of slides.'),  # noqa
     )
     )
     animation = models.CharField(
     animation = models.CharField(
         blank=True,
         blank=True,
@@ -297,6 +299,7 @@ class Footer(models.Model):
     def __str__(self):
     def __str__(self):
         return self.name
         return self.name
 
 
+
 @register_snippet
 @register_snippet
 class ReusableContent(models.Model):
 class ReusableContent(models.Model):
     """
     """
@@ -375,11 +378,37 @@ class CoderedEmail(ClusterableModel):
         abstract = True
         abstract = True
         verbose_name = _('CodeRed Email')
         verbose_name = _('CodeRed Email')
 
 
-    to_address = models.CharField(max_length=255, blank=True, verbose_name=_('To Addresses'), help_text=_('Separate multiple email addresses with commas.'))
-    from_address = models.CharField(max_length=255, blank=True, verbose_name=_('From Address'), help_text=_('For example: "sender@example.com" or "Sender Name <sender@example.com>" (without quotes).'))
-    reply_address = models.CharField(max_length=255, blank=True, verbose_name=_('Reply-To Address'), help_text=_('Separate multiple email addresses with commas.'))
-    cc_address = models.CharField(max_length=255, blank=True, verbose_name=_('CC'), help_text=_('Separate multiple email addresses with commas.'))
-    bcc_address = models.CharField(max_length=255, blank=True, verbose_name=_('BCC'), help_text=_('Separate multiple email addresses with commas.'))
+    to_address = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('To Addresses'),
+        help_text=_('Separate multiple email addresses with commas.')
+    )
+    from_address = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('From Address'),
+        help_text=_(
+            'For example: "sender@example.com" or "Sender Name <sender@example.com>" (without quotes).')  # noqa
+    )
+    reply_address = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('Reply-To Address'),
+        help_text=_('Separate multiple email addresses with commas.')
+    )
+    cc_address = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('CC'),
+        help_text=_('Separate multiple email addresses with commas.')
+    )
+    bcc_address = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('BCC'),
+        help_text=_('Separate multiple email addresses with commas.')
+    )
     subject = models.CharField(max_length=255, blank=True, verbose_name=_('Subject'))
     subject = models.CharField(max_length=255, blank=True, verbose_name=_('Subject'))
     body = models.TextField(blank=True, verbose_name=_('Body'))
     body = models.TextField(blank=True, verbose_name=_('Body'))
 
 

+ 4 - 3
coderedcms/models/tests/test_page_models.py

@@ -26,6 +26,7 @@ from coderedcms.tests.testapp.models import (
     WebPage
     WebPage
 )
 )
 
 
+
 class BasicPageTestCase():
 class BasicPageTestCase():
     """
     """
     This is a testing mixin used to run common tests for basic versions of page types.
     This is a testing mixin used to run common tests for basic versions of page types.
@@ -59,7 +60,7 @@ class AbstractPageTestCase():
 
 
     def test_not_available(self):
     def test_not_available(self):
         """
         """
-        Tests to make sure the page is not creatable and not in CodeRed CMS's global list of page models.
+        Tests to make sure the page is not creatable and not in CodeRed CMS's global list of page models.  # noqa
         """
         """
         self.assertFalse(self.model.is_creatable)
         self.assertFalse(self.model.is_creatable)
         self.assertFalse(self.model in get_page_models())
         self.assertFalse(self.model in get_page_models())
@@ -100,7 +101,7 @@ class ConcreteFormPageTestCase(ConcreteBasicPageTestCase):
         """
         """
         Test to check if the default spam catching works.
         Test to check if the default spam catching works.
         """
         """
-        response = self.client.post(self.basic_page.url, {'cr-decoy-comments': 'This is Spam'}, follow=True)
+        response = self.client.post(self.basic_page.url, {'cr-decoy-comments': 'This is Spam'}, follow=True)  # noqa
         messages = list(response.context['messages'])
         messages = list(response.context['messages'])
         self.assertEqual(len(messages), 1)
         self.assertEqual(len(messages), 1)
         self.assertEqual(str(messages[0]), self.basic_page.get_spam_message())
         self.assertEqual(str(messages[0]), self.basic_page.get_spam_message())
@@ -190,4 +191,4 @@ class LocationPageTestCase(ConcreteBasicPageTestCase, WagtailPageTests):
 
 
 
 
 class StreamFormPageTestCase(ConcreteFormPageTestCase, WagtailPageTests):
 class StreamFormPageTestCase(ConcreteFormPageTestCase, WagtailPageTests):
-    model = StreamFormPage
+    model = StreamFormPage

+ 25 - 9
coderedcms/models/wagtailsettings_models.py

@@ -123,7 +123,7 @@ class LayoutSettings(BaseSetting):
         choices=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES'],
         choices=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES'],
         default=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_DEFAULT'],
         default=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_DEFAULT'],
         verbose_name=_('Navbar color scheme'),
         verbose_name=_('Navbar color scheme'),
-        help_text=_('Optimizes text and other navbar elements for use with light or dark backgrounds.'),
+        help_text=_('Optimizes text and other navbar elements for use with light or dark backgrounds.'),  # noqa
     )
     )
     navbar_class = models.CharField(
     navbar_class = models.CharField(
         blank=True,
         blank=True,
@@ -223,7 +223,9 @@ class AnalyticsSettings(BaseSetting):
     ga_track_button_clicks = models.BooleanField(
     ga_track_button_clicks = models.BooleanField(
         default=False,
         default=False,
         verbose_name=_('Track button clicks'),
         verbose_name=_('Track button clicks'),
-        help_text=_('Track all button clicks using Google Analytics event tracking. Event tracking details can be specified in each button’s advanced settings options.'),
+        help_text=_(
+            "Track all button clicks using Google Analytics event tracking. Event tracking details can be specified in each button’s advanced settings options."  # noqa
+        ),
     )
     )
 
 
     panels = [
     panels = [
@@ -248,7 +250,9 @@ class ADASettings(BaseSetting):
     skip_navigation = models.BooleanField(
     skip_navigation = models.BooleanField(
         default=False,
         default=False,
         verbose_name=_('Show skip navigation link'),
         verbose_name=_('Show skip navigation link'),
-        help_text=_('Shows a "Skip Navigation" link above the navbar that takes you directly to the main content.'),
+        help_text=_(
+            "Shows a 'Skip Navigation' link above the navbar that takes you directly to the main content."  # noqa
+        ),
     )
     )
 
 
     panels = [
     panels = [
@@ -273,7 +277,9 @@ class GeneralSettings(BaseSetting):
         blank=True,
         blank=True,
         max_length=255,
         max_length=255,
         verbose_name=_('From email address'),
         verbose_name=_('From email address'),
-        help_text=_('The default email address this site appears to send from. For example: "sender@example.com" or "Sender Name <sender@example.com>" (without quotes)'),
+        help_text=_(
+            "The default email address this site appears to send from. For example: 'sender@example.com' or 'Sender Name <sender@example.com>' (without quotes)"  # noqa
+        ),
     )
     )
     search_num_results = models.PositiveIntegerField(
     search_num_results = models.PositiveIntegerField(
         default=10,
         default=10,
@@ -311,22 +317,30 @@ class SeoSettings(BaseSetting):
     og_meta = models.BooleanField(
     og_meta = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Use OpenGraph Markup'),
         verbose_name=_('Use OpenGraph Markup'),
-        help_text=_('Show an optimized preview when linking to this site on Facebook, Linkedin, Twitter, and others. See http://ogp.me/.'),
+        help_text=_(
+            "Show an optimized preview when linking to this site on Facebook, Linkedin, Twitter, and others. See http://ogp.me/."  # noqa
+        ),
     )
     )
     twitter_meta = models.BooleanField(
     twitter_meta = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Use Twitter Markup'),
         verbose_name=_('Use Twitter Markup'),
-        help_text=_('Shows content as a "card" when linking to this site on Twitter. See https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards.'),
+        help_text=_(
+            "Shows content as a 'card' when linking to this site on Twitter. See https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards."  # noqa
+        ),
     )
     )
     struct_meta = models.BooleanField(
     struct_meta = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Use Structured Data'),
         verbose_name=_('Use Structured Data'),
-        help_text=_('Optimizes information about your organization for search engines. See https://schema.org/.'),
+        help_text=_(
+            "Optimizes information about your organization for search engines. See https://schema.org/."  # noqa
+        ),
     )
     )
     amp_pages = models.BooleanField(
     amp_pages = models.BooleanField(
         default=True,
         default=True,
         verbose_name=_('Use AMP Pages'),
         verbose_name=_('Use AMP Pages'),
-        help_text=_('Generates an alternate AMP version of Article pages that are preferred by search engines. See https://www.ampproject.org/'),
+        help_text=_(
+            "Generates an alternate AMP version of Article pages that are preferred by search engines. See https://www.ampproject.org/"  # noqa
+        ),
     )
     )
 
 
     panels = [
     panels = [
@@ -336,7 +350,9 @@ class SeoSettings(BaseSetting):
                 FieldPanel('twitter_meta'),
                 FieldPanel('twitter_meta'),
                 FieldPanel('struct_meta'),
                 FieldPanel('struct_meta'),
                 FieldPanel('amp_pages'),
                 FieldPanel('amp_pages'),
-                HelpPanel(content=_('If these settings are enabled, the corresponding values in each page’s SEO tab are used.')),
+                HelpPanel(content=_(
+                    "If these settings are enabled, the corresponding values in each page’s SEO tab are used."  # noqa
+                )),
             ],
             ],
             heading=_('Search Engine Optimization')
             heading=_('Search Engine Optimization')
         )
         )

+ 2 - 2
coderedcms/project_template/project_name/settings/dev.py

@@ -1,4 +1,4 @@
-from .base import *
+from .base import *  # noqa
 
 
 # SECURITY WARNING: don't run with debug turned on in production!
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 DEBUG = True
@@ -13,6 +13,6 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 WAGTAIL_CACHE = False
 WAGTAIL_CACHE = False
 
 
 try:
 try:
-    from .local_settings import *
+    from .local_settings import *  # noqa
 except ImportError:
 except ImportError:
     pass
     pass

+ 3 - 3
coderedcms/project_template/project_name/settings/prod.py

@@ -1,4 +1,4 @@
-from .base import *
+from .base import *  # noqa
 
 
 # SECURITY WARNING: don't run with debug turned on in production!
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = False
 DEBUG = False
@@ -67,13 +67,13 @@ TEMPLATES = [
 CACHES = {
 CACHES = {
     'default': {
     'default': {
         'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
         'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
-        'LOCATION': os.path.join(BASE_DIR, 'cache'),
+        'LOCATION': os.path.join(BASE_DIR, 'cache'),  # noqa
         'KEY_PREFIX': 'coderedcms',
         'KEY_PREFIX': 'coderedcms',
         'TIMEOUT': 14400,  # in seconds
         'TIMEOUT': 14400,  # in seconds
     }
     }
 }
 }
 
 
 try:
 try:
-    from .local_settings import *
+    from .local_settings import *  # noqa
 except ImportError:
 except ImportError:
     pass
     pass

+ 1 - 1
coderedcms/project_template/project_name/urls.py

@@ -24,7 +24,7 @@ urlpatterns = [
 
 
     # Alternatively, if you want CMS pages to be served from a subpath
     # Alternatively, if you want CMS pages to be served from a subpath
     # of your site, rather than the site root:
     # of your site, rather than the site root:
-    #    re_path(r'^pages/', include(codered_urls)),
+    #    re_path(r"^pages/", include(codered_urls)),
 ]
 ]
 
 
 
 

+ 11 - 7
coderedcms/settings.py

@@ -3,14 +3,16 @@ from django.conf import settings
 from django.utils.lru_cache import lru_cache
 from django.utils.lru_cache import lru_cache
 
 
 
 
-PROJECT_DIR = settings.PROJECT_DIR if getattr(settings, 'PROJECT_DIR') else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+PROJECT_DIR = settings.PROJECT_DIR if getattr(settings, 'PROJECT_DIR') else os.path.dirname(
+    os.path.dirname(os.path.abspath(__file__))
+)
 BASE_DIR = settings.BASE_DIR if getattr(settings, 'BASE_DIR') else os.path.dirname(PROJECT_DIR)
 BASE_DIR = settings.BASE_DIR if getattr(settings, 'BASE_DIR') else os.path.dirname(PROJECT_DIR)
 
 
 DEFAULTS = {
 DEFAULTS = {
     'PROTECTED_MEDIA_URL': '/protected/',
     'PROTECTED_MEDIA_URL': '/protected/',
     'PROTECTED_MEDIA_ROOT': os.path.join(BASE_DIR, 'protected'),
     'PROTECTED_MEDIA_ROOT': os.path.join(BASE_DIR, 'protected'),
     'PROTECTED_MEDIA_UPLOAD_WHITELIST': [],
     'PROTECTED_MEDIA_UPLOAD_WHITELIST': [],
-    'PROTECTED_MEDIA_UPLOAD_BLACKLIST': ['.sh', '.exe', '.bat', '.ps1', '.app', '.jar', '.py', '.php', '.pl', '.rb'],
+    'PROTECTED_MEDIA_UPLOAD_BLACKLIST': ['.sh', '.exe', '.bat', '.ps1', '.app', '.jar', '.py', '.php', '.pl', '.rb'],  # noqa
 
 
     'FRONTEND_BTN_SIZE_DEFAULT': '',
     'FRONTEND_BTN_SIZE_DEFAULT': '',
     'FRONTEND_BTN_SIZE_CHOICES': (
     'FRONTEND_BTN_SIZE_CHOICES': (
@@ -95,7 +97,7 @@ DEFAULTS = {
         ('navbar-expand-xl', 'xl - Show on extra large screens (desktop, wide monitor)'),
         ('navbar-expand-xl', 'xl - Show on extra large screens (desktop, wide monitor)'),
     ),
     ),
 
 
-    'FRONTEND_THEME_HELP': 'Change the color palette of your site with a Bootstrap theme. Powered by Bootswatch https://bootswatch.com/.',
+    'FRONTEND_THEME_HELP': "Change the color palette of your site with a Bootstrap theme. Powered by Bootswatch https://bootswatch.com/.",  # noqa
     'FRONTEND_THEME_DEFAULT': '',
     'FRONTEND_THEME_DEFAULT': '',
     'FRONTEND_THEME_CHOICES': (
     'FRONTEND_THEME_CHOICES': (
         ('', 'Default - Classic Bootstrap'),
         ('', 'Default - Classic Bootstrap'),
@@ -140,9 +142,12 @@ DEFAULTS = {
             ('coderedcms/blocks/pagelist_block.html', 'General, simple list'),
             ('coderedcms/blocks/pagelist_block.html', 'General, simple list'),
             ('coderedcms/blocks/pagelist_list_group.html', 'General, list group navigation panel'),
             ('coderedcms/blocks/pagelist_list_group.html', 'General, list group navigation panel'),
             ('coderedcms/blocks/pagelist_article_media.html', 'Article, media format'),
             ('coderedcms/blocks/pagelist_article_media.html', 'Article, media format'),
-            ('coderedcms/blocks/pagelist_article_card_group.html', 'Article, card group - attached cards of equal size'),
-            ('coderedcms/blocks/pagelist_article_card_deck.html', 'Article, card deck - separate cards of equal size'),
-            ('coderedcms/blocks/pagelist_article_card_columns.html', 'Article, card masonry - fluid brick pattern'),
+            ('coderedcms/blocks/pagelist_article_card_group.html',
+                'Article, card group - attached cards of equal size'),
+            ('coderedcms/blocks/pagelist_article_card_deck.html',
+             'Article, card deck - separate cards of equal size'),
+            ('coderedcms/blocks/pagelist_article_card_columns.html',
+             'Article, card masonry - fluid brick pattern'),
         ),
         ),
         'pagepreviewblock': (
         'pagepreviewblock': (
             ('coderedcms/blocks/pagepreview_card.html', 'Card'),
             ('coderedcms/blocks/pagepreview_card.html', 'Card'),
@@ -163,7 +168,6 @@ DEFAULTS = {
             ('coderedcms/pages/home_page.html', 'Home page without title and cover image'),
             ('coderedcms/pages/home_page.html', 'Home page without title and cover image'),
             ('coderedcms/pages/base.html', 'Blank page - no navbar or footer'),
             ('coderedcms/pages/base.html', 'Blank page - no navbar or footer'),
         ),
         ),
-
     },
     },
 
 
     'BANNER': None,
     'BANNER': None,

+ 1 - 1
coderedcms/templatetags/coderedcms_tags.py

@@ -152,7 +152,7 @@ def query_update(querydict, key=None, value=None):
         else:
         else:
             try:
             try:
                 del(get[key])
                 del(get[key])
-            except:
+            except KeyError:
                 pass
                 pass
     return get
     return get
 
 

+ 2 - 6
coderedcms/tests/settings.py

@@ -12,7 +12,7 @@ https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/
 
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 import os
 import os
-from django.utils.translation import gettext_lazy as _
+from django.utils.translation import gettext_lazy as _  # noqa
 
 
 
 
 PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -80,7 +80,7 @@ MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.middleware.security.SecurityMiddleware',
 
 
     # Error reporting. Uncomment this to recieve emails when a 404 is triggered.
     # Error reporting. Uncomment this to recieve emails when a 404 is triggered.
-    #'django.middleware.common.BrokenLinkEmailsMiddleware',
+    # 'django.middleware.common.BrokenLinkEmailsMiddleware',
 
 
     # CMS functionality
     # CMS functionality
     'wagtail.core.middleware.SiteMiddleware',
     'wagtail.core.middleware.SiteMiddleware',
@@ -147,10 +147,6 @@ AUTH_PASSWORD_VALIDATORS = [
 
 
 LANGUAGE_CODE = 'en-us'
 LANGUAGE_CODE = 'en-us'
 
 
-LANGUAGES = [
-    ('en-us', _('English')),
-]
-
 TIME_ZONE = 'America/New_York'
 TIME_ZONE = 'America/New_York'
 
 
 USE_I18N = True
 USE_I18N = True

+ 0 - 1
coderedcms/tests/urls.py

@@ -1,4 +1,3 @@
-from django.conf import settings
 from django.urls import include, path, re_path
 from django.urls import include, path, re_path
 from django.contrib import admin
 from django.contrib import admin
 from wagtail.documents import urls as wagtaildocs_urls
 from wagtail.documents import urls as wagtaildocs_urls

+ 11 - 5
coderedcms/urls.py

@@ -18,15 +18,21 @@ urlpatterns = [
     # CodeRed custom URLs
     # CodeRed custom URLs
     re_path(r'^sitemap\.xml$', cache_page(sitemap), name='codered_sitemap'),
     re_path(r'^sitemap\.xml$', cache_page(sitemap), name='codered_sitemap'),
     re_path(r'^robots\.txt$', cache_page(robots), name='codered_robots'),
     re_path(r'^robots\.txt$', cache_page(robots), name='codered_robots'),
-    re_path(r'^{0}(?P<path>.*)$'.format(cr_settings['PROTECTED_MEDIA_URL'].lstrip('/')), serve_protected_file, name="serve_protected_file"),
+    re_path(r'^{0}(?P<path>.*)$'.format(
+        cr_settings['PROTECTED_MEDIA_URL'].lstrip('/')),
+        serve_protected_file,
+        name="serve_protected_file"
+    ),
 
 
     # Event/Calendar URLs
     # Event/Calendar URLs
-    path('ical/generate/single/', event_generate_single_ical_for_event, name='event_generate_single_ical'),
-    path('ical/generate/recurring/', event_generate_recurring_ical_for_event, name='event_generate_recurring_ical'),
-    path('ical/generate/calendar/', event_generate_ical_for_calendar, name='event_generate_ical_for_calendar'),
+    path('ical/generate/single/', event_generate_single_ical_for_event,
+         name='event_generate_single_ical'),
+    path('ical/generate/recurring/', event_generate_recurring_ical_for_event,
+         name='event_generate_recurring_ical'),
+    path('ical/generate/calendar/', event_generate_ical_for_calendar,
+         name='event_generate_ical_for_calendar'),
     path('ajax/calendar/events/', event_get_calendar_events, name='event_get_calendar_events'),
     path('ajax/calendar/events/', event_get_calendar_events, name='event_get_calendar_events'),
 
 
     # Wagtail
     # Wagtail
     re_path(r'', include(wagtailcore_urls)),
     re_path(r'', include(wagtailcore_urls)),
-
 ]
 ]

+ 11 - 1
coderedcms/utils.py

@@ -5,11 +5,18 @@ from django.utils.html import mark_safe
 
 
 from coderedcms.settings import cr_settings
 from coderedcms.settings import cr_settings
 
 
+
 def get_protected_media_link(request, path, render_link=False):
 def get_protected_media_link(request, path, render_link=False):
     if render_link:
     if render_link:
-        return mark_safe("<a href='{0}{1}'>{0}{1}</a>".format(request.build_absolute_uri('/')[:-1], path))
+        return mark_safe(
+            "<a href='{0}{1}'>{0}{1}</a>".format(
+                request.build_absolute_uri('/')[:-1],
+                path
+            )
+        )
     return "{0}{1}".format(request.build_absolute_uri('/')[:-1], path)
     return "{0}{1}".format(request.build_absolute_uri('/')[:-1], path)
 
 
+
 def uri_validator(possible_uri):
 def uri_validator(possible_uri):
     validate = URLValidator()
     validate = URLValidator()
     try:
     try:
@@ -18,6 +25,7 @@ def uri_validator(possible_uri):
     except ValidationError:
     except ValidationError:
         return False
         return False
 
 
+
 def attempt_protected_media_value_conversion(request, value):
 def attempt_protected_media_value_conversion(request, value):
     try:
     try:
         if value.startswith(cr_settings['PROTECTED_MEDIA_URL']):
         if value.startswith(cr_settings['PROTECTED_MEDIA_URL']):
@@ -28,6 +36,7 @@ def attempt_protected_media_value_conversion(request, value):
 
 
     return value
     return value
 
 
+
 def fix_ical_datetime_format(dt_str):
 def fix_ical_datetime_format(dt_str):
     """
     """
     ICAL generation gives timezones in the format of 2018-06-30T14:00:00-04:00.
     ICAL generation gives timezones in the format of 2018-06-30T14:00:00-04:00.
@@ -39,6 +48,7 @@ def fix_ical_datetime_format(dt_str):
         return dt_str
         return dt_str
     return dt_str
     return dt_str
 
 
+
 def convert_to_amp(value):
 def convert_to_amp(value):
     """
     """
     Function that converts non-amp compliant html to valid amp html.
     Function that converts non-amp compliant html to valid amp html.

+ 19 - 14
coderedcms/views.py

@@ -7,13 +7,12 @@ from datetime import datetime
 from django.http import Http404, HttpResponse, JsonResponse
 from django.http import Http404, HttpResponse, JsonResponse
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.decorators import login_required
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
-from django.core.paginator import Paginator
+from django.core.paginator import Paginator, InvalidPage, EmptyPage, PageNotAnInteger
 from django.shortcuts import redirect, render
 from django.shortcuts import redirect, render
 from django.utils.translation import ungettext, ugettext_lazy as _
 from django.utils.translation import ungettext, ugettext_lazy as _
 from icalendar import Calendar
 from icalendar import Calendar
 
 
 from wagtail.admin import messages
 from wagtail.admin import messages
-from wagtail.core.models import Page
 from wagtail.search.backends import db, get_search_backend
 from wagtail.search.backends import db, get_search_backend
 from wagtail.search.models import Query
 from wagtail.search.models import Query
 
 
@@ -24,8 +23,6 @@ from coderedcms.importexport import convert_csv_to_json, import_pages, ImportPag
 from coderedcms.settings import cr_settings
 from coderedcms.settings import cr_settings
 
 
 
 
-
-
 def search(request):
 def search(request):
     """
     """
     Searches pages across the entire site.
     Searches pages across the entire site.
@@ -62,7 +59,7 @@ def search(request):
         if backend.__class__ == db.SearchBackend and db_models:
         if backend.__class__ == db.SearchBackend and db_models:
             for model in db_models:
             for model in db_models:
                 # if search_model is provided, only search on that model
                 # if search_model is provided, only search on that model
-                if not search_model or search_model == ContentType.objects.get_for_model(model).model:
+                if not search_model or search_model == ContentType.objects.get_for_model(model).model:  # noqa
                     curr_results = model.objects.live().search(search_query)
                     curr_results = model.objects.live().search(search_query)
                     if results:
                     if results:
                         results = list(chain(results, curr_results))
                         results = list(chain(results, curr_results))
@@ -75,18 +72,23 @@ def search(request):
                 try:
                 try:
                     model = ContentType.objects.get(model=search_model).model_class()
                     model = ContentType.objects.get(model=search_model).model_class()
                     results = model.objects.live().search(search_query)
                     results = model.objects.live().search(search_query)
-                except:
+                except search_model.DoesNotExist:
                     results = None
                     results = None
             else:
             else:
-                results = CoderedPage.objects.live().order_by('-last_published_at').search(search_query)
+                results = CoderedPage.objects.live().order_by('-last_published_at').search(search_query)  # noqa
 
 
         # paginate results
         # paginate results
         if results:
         if results:
-            paginator = Paginator(results, GeneralSettings.for_site(request.site).search_num_results)
+            paginator = Paginator(results, GeneralSettings.for_site(
+                request.site).search_num_results)
             page = request.GET.get('p', 1)
             page = request.GET.get('p', 1)
             try:
             try:
                 results_paginated = paginator.page(page)
                 results_paginated = paginator.page(page)
-            except:
+            except PageNotAnInteger:
+                results_paginated = paginator.page(1)
+            except EmptyPage:
+                results_paginated = paginator.page(1)
+            except InvalidPage:
                 results_paginated = paginator.page(1)
                 results_paginated = paginator.page(1)
 
 
         # Log the query so Wagtail can suggest promoted results
         # Log the query so Wagtail can suggest promoted results
@@ -159,7 +161,7 @@ def event_generate_recurring_ical_for_event(request):
             try:
             try:
                 event = event_page_model.objects.get(pk=event_pk)
                 event = event_page_model.objects.get(pk=event_pk)
                 break
                 break
-            except event_page_modal.DoesNotExist:
+            except event_page_model.DoesNotExist:
                 pass
                 pass
         ical = Calendar()
         ical = Calendar()
         for e in event.create_recurring_ical():
         for e in event.create_recurring_ical():
@@ -207,15 +209,18 @@ def event_get_calendar_events(request):
 @login_required
 @login_required
 def import_pages_from_csv_file(request):
 def import_pages_from_csv_file(request):
     """
     """
-    Overwrite of the `import_pages` view from wagtailimportexport.  By default, the `import_pages` view
-    expects a json file to be uploaded.  This view converts the uploaded csv into the json format that
-    the importer expects.
+    Overwrite of the `import_pages` view from wagtailimportexport.  By default, the `import_pages`
+    view expects a json file to be uploaded.  This view converts the uploaded csv into the json
+    format that the importer expects.
     """
     """
 
 
     if request.method == 'POST':
     if request.method == 'POST':
         form = ImportPagesFromCSVFileForm(request.POST, request.FILES)
         form = ImportPagesFromCSVFileForm(request.POST, request.FILES)
         if form.is_valid():
         if form.is_valid():
-            import_data = convert_csv_to_json(form.cleaned_data['file'].read().decode('utf-8').splitlines(), form.cleaned_data['page_type'])
+            import_data = convert_csv_to_json(
+                form.cleaned_data['file'].read().decode('utf-8').splitlines(),
+                form.cleaned_data['page_type']
+            )
             parent_page = form.cleaned_data['parent_page']
             parent_page = form.cleaned_data['parent_page']
             try:
             try:
                 page_count = import_pages(import_data, parent_page)
                 page_count = import_pages(import_data, parent_page)

+ 5 - 8
coderedcms/wagtail_flexible_forms/models.py

@@ -12,7 +12,6 @@ from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.humanize.templatetags.humanize import naturaltime
 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 (
@@ -20,7 +19,7 @@ from django.db.models import (
     QuerySet,
     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, post_save
+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 Form, ImageField, FileField, URLField, EmailField
 from django.http import HttpResponseRedirect
 from django.http import HttpResponseRedirect
@@ -295,7 +294,7 @@ class SessionFormSubmission(AbstractFormSubmission):
         verbose_name_plural = _('form submissions')
         verbose_name_plural = _('form submissions')
         unique_together = (('page', 'session_key'),
         unique_together = (('page', 'session_key'),
                            ('page', 'user'))
                            ('page', 'user'))
-        abstract=True
+        abstract = True
 
 
     @property
     @property
     def is_complete(self):
     def is_complete(self):
@@ -535,7 +534,7 @@ class SubmissionRevision(Model):
 
 
     class Meta:
     class Meta:
         ordering = ('-created_at',)
         ordering = ('-created_at',)
-        abstract=True
+        abstract = True
 
 
     @staticmethod
     @staticmethod
     def get_filters_for(submission):
     def get_filters_for(submission):
@@ -598,9 +597,7 @@ class SubmissionRevision(Model):
         if not summary:  # Nothing changed.
         if not summary:  # Nothing changed.
             return
             return
         filters.update(
         filters.update(
-            type=revision_type,
-            data=json.dumps(data, cls=StreamFormJSONEncoder),
-            summary=summary,
+            type=revision_type, data=json.dumps(data, cls=StreamFormJSONEncoder), summary=summary
         )
         )
         return cls.objects.create(**filters)
         return cls.objects.create(**filters)
 
 
@@ -838,4 +835,4 @@ class AbstractStreamForm(StreamFormMixin, AbstractForm):
 
 
 class AbstractEmailStreamForm(StreamFormMixin, AbstractEmailForm):
 class AbstractEmailStreamForm(StreamFormMixin, AbstractEmailForm):
     class Meta:
     class Meta:
-        abstract = True
+        abstract = True

+ 5 - 5
coderedcms/wagtail_flexible_forms/wagtail_hooks.py

@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
 from wagtail.contrib.modeladmin.helpers import (
 from wagtail.contrib.modeladmin.helpers import (
     PermissionHelper, PagePermissionHelper, PageAdminURLHelper, AdminURLHelper,
     PermissionHelper, PagePermissionHelper, PageAdminURLHelper, AdminURLHelper,
     ButtonHelper)
     ButtonHelper)
-from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
+from wagtail.contrib.modeladmin.options import ModelAdmin
 from wagtail.contrib.modeladmin.views import IndexView, InstanceSpecificView
 from wagtail.contrib.modeladmin.views import IndexView, InstanceSpecificView
 from wagtail.admin import messages
 from wagtail.admin import messages
 from wagtail.core import hooks
 from wagtail.core import hooks
@@ -168,7 +168,7 @@ class SubmissionButtonHelper(ButtonHelper):
                                       classnames_exclude=classnames_exclude)
                                       classnames_exclude=classnames_exclude)
 
 
     def approve_button(self, pk, classnames_add=None,
     def approve_button(self, pk, classnames_add=None,
-                        classnames_exclude=None):
+                       classnames_exclude=None):
         if classnames_add is None:
         if classnames_add is None:
             classnames_add = []
             classnames_add = []
         if 'button-secondary' in classnames_add:
         if 'button-secondary' in classnames_add:
@@ -288,11 +288,11 @@ class SubmissionAdmin(ModelAdmin):
         form_page = self.get_form_page(request)
         form_page = self.get_form_page(request)
         if form_page is None:
         if form_page is None:
             return self.list_display
             return self.list_display
-        l = []
+        fields = []
         for name, label in form_page.get_data_fields():
         for name, label in form_page.get_data_fields():
-            l.append(name)
+            fields.append(name)
             self.add_data_bridge(name, label)
             self.add_data_bridge(name, label)
-        return l
+        return fields
 
 
     def set_status_view(self, request, instance_pk):
     def set_status_view(self, request, instance_pk):
         kwargs = {'model_admin': self, 'instance_pk': instance_pk}
         kwargs = {'model_admin': self, 'instance_pk': instance_pk}

+ 20 - 8
coderedcms/wagtail_hooks.py

@@ -6,23 +6,27 @@ from django.http.response import HttpResponse
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.html import format_html, mark_safe
 from django.utils.html import format_html, mark_safe
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
-from wagtail.contrib.forms.models import AbstractForm
-from wagtail.contrib.modeladmin.options import modeladmin_register
 from wagtail.core import hooks
 from wagtail.core import hooks
 from wagtail.core.models import UserPagePermissionsProxy, get_page_models
 from wagtail.core.models import UserPagePermissionsProxy, get_page_models
 from wagtailcache.cache import clear_cache
 from wagtailcache.cache import clear_cache
 
 
-from coderedcms import utils
 from coderedcms.wagtail_flexible_forms.wagtail_hooks import FormAdmin, SubmissionAdmin
 from coderedcms.wagtail_flexible_forms.wagtail_hooks import FormAdmin, SubmissionAdmin
 
 
+
 @hooks.register('insert_global_admin_css')
 @hooks.register('insert_global_admin_css')
 def global_admin_css():
 def global_admin_css():
-    return format_html('<link rel="stylesheet" type="text/css" href="{}">', static('coderedcms/css/codered-admin.css'))
+    return format_html(
+        '<link rel="stylesheet" type="text/css" href="{}">',
+        static('coderedcms/css/codered-admin.css')
+    )
 
 
 
 
 @hooks.register('insert_editor_css')
 @hooks.register('insert_editor_css')
 def editor_css():
 def editor_css():
-    return format_html('<link rel="stylesheet" type="text/css" href="{}">', static('coderedcms/css/codered-editor.css'))
+    return format_html(
+        '<link rel="stylesheet" type="text/css" href="{}">',
+        static('coderedcms/css/codered-editor.css')
+    )
 
 
 
 
 @hooks.register('insert_editor_js')
 @hooks.register('insert_editor_js')
@@ -58,10 +62,12 @@ def codered_forms(user, editable_forms):
 
 
     return editable_forms
     return editable_forms
 
 
+
 @hooks.register('before_serve_document')
 @hooks.register('before_serve_document')
 def serve_document_directly(document, request):
 def serve_document_directly(document, request):
     """
     """
-    This hook prevents documents from being downloaded unless specified by an <a> tag with the download attribute.
+    This hook prevents documents from being downloaded unless
+    specified by an <a> tag with the download attribute.
     """
     """
     content_type, content_encoding = mimetypes.guess_type(document.filename)
     content_type, content_encoding = mimetypes.guess_type(document.filename)
     response = HttpResponse(document.file.read(), content_type=content_type)
     response = HttpResponse(document.file.read(), content_type=content_type)
@@ -94,10 +100,16 @@ class CoderedFormAdmin(FormAdmin):
         actions = []
         actions = []
         if issubclass(type(obj.specific), CoderedFormPage):
         if issubclass(type(obj.specific), CoderedFormPage):
             actions.append(
             actions.append(
-                '<a href="{0}">{1}</a>'.format(reverse('wagtailforms:list_submissions', args=(obj.pk,)), _('See all Submissions'))
+                '<a href="{0}">{1}</a>'.format(reverse(
+                    'wagtailforms:list_submissions',
+                    args=(obj.pk,)),
+                    _('See all Submissions')
+                )
             )
             )
             actions.append(
             actions.append(
-                '<a href="{0}">{1}</a>'.format(reverse('wagtailadmin_pages:edit', args=(obj.pk,)), _('Edit this form page'))
+                '<a href="{0}">{1}</a>'.format(
+                    reverse("wagtailadmin_pages:edit", args=(obj.pk,)), _("Edit this form page")
+                )
             )
             )
         elif issubclass(type(obj.specific), CoderedStreamFormPage):
         elif issubclass(type(obj.specific), CoderedStreamFormPage):
             actions.append(self.unprocessed_submissions_link(obj))
             actions.append(self.unprocessed_submissions_link(obj))

+ 2 - 2
docs/conf.py

@@ -118,7 +118,7 @@ html_static_path = ['_static']
 # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
 # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
 # 'searchbox.html']``.
 # 'searchbox.html']``.
 #
 #
-html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'relations.html', 'versions.html'] }
+html_sidebars = {'**': ['searchbox.html', 'globaltoc.html', 'relations.html', 'versions.html']}
 
 
 html_context = {
 html_context = {
     'css_files': ['_static/docs.css'],
     'css_files': ['_static/docs.css'],
@@ -199,4 +199,4 @@ epub_title = project
 # epub_uid = ''
 # epub_uid = ''
 
 
 # A list of files that should not be packed into the epub file.
 # A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
+epub_exclude_files = ['search.html']

+ 26 - 0
pyproject.toml

@@ -0,0 +1,26 @@
+[tool.black]
+line-length = 100
+target-version = ['py37']
+include = '\.pyi?$'
+exclude = '''
+
+(
+  /(
+      \.eggs         # exclude a few common directories in the
+    | \.git          # root of the project
+    | \.hg
+    | \.mypy_cache
+    | \.tox
+    | \.venv
+    | _build
+    | buck-out
+    | build
+    | dist
+    | coderedcms/project_template
+    | coderedcms/tests/settings.py
+    | .*/migrations
+    | \.github
+    | ci
+  )/
+)
+'''

+ 2 - 2
setup.cfg

@@ -1,3 +1,3 @@
 [flake8]
 [flake8]
-exclude = coderedcms/project_template/*,migrations
-max-line-length = 120
+exclude = coderedcms/project_template/*,migrations,schema.py
+max-line-length = 100

+ 17 - 14
setup.py

@@ -2,7 +2,7 @@ import os
 from setuptools import find_packages, setup
 from setuptools import find_packages, setup
 from coderedcms import __version__
 from coderedcms import __version__
 
 
-with open(os.path.join(os.path.dirname(__file__), 'README.md'), encoding='utf8') as readme:
+with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8") as readme:
     README = readme.read()
     README = readme.read()
 
 
 # allow setup.py to be run from any path
 # allow setup.py to be run from any path
@@ -53,20 +53,23 @@ setup(
         'wagtail-import-export>=0.1,<0.2'
         'wagtail-import-export>=0.1,<0.2'
     ],
     ],
     extras_require={
     extras_require={
-        'dev': [
-            'flake8',
-            'libsass',
-            'pytest-cov',
-            'pytest-django',
-            'sphinx',
-            'twine',
-            'wheel'
+        "dev": [
+            # "autopep8",
+            # "black",
+            "flake8",
+            "libsass",
+            "pytest-cov",
+            "pytest-django",
+            "sphinx",
+            "twine",
+            "wheel",
+        ],  # noqa
+        "ci_tests": ["pytest-cov", "pytest-django"],
+        "ci_style": [
+            # "autopep8",
+            # "black",
+            "flake8"
         ],
         ],
-        'ci': [
-            'flake8',
-            'pytest-cov',
-            'pytest-django',
-        ]
     },
     },
     entry_points="""
     entry_points="""
             [console_scripts]
             [console_scripts]