Skip to content

Commit

Permalink
style: add black to quality and format code (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuchfarber authored Sep 10, 2021
1 parent aecef71 commit d97f60a
Show file tree
Hide file tree
Showing 28 changed files with 208 additions and 147 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ docs/notices.*.rst
# Private requirements
requirements/private.in
requirements/private.txt

# Virtual environments
venv/
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ upgrade: ## update the requirements/*.txt files with the latest packages satisfy
quality: ## check coding style with pycodestyle and pylint
tox -e quality

quality-fix: ## Fix quality failures that are automatable
isort tests notices manage.py setup.py test_settings.py
black .

quality-requirements: requirements ## Installs quality-related requirements
pip-sync requirements/quality.txt requirements/private.*

pii_check: ## check for PII annotations on all Django models
tox -e pii_check

Expand Down
92 changes: 49 additions & 43 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def get_version(*file_paths):
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError('Unable to find version string.')
raise RuntimeError("Unable to find version string.")


REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(REPO_ROOT)

VERSION = get_version('../notices', '__init__.py')
VERSION = get_version("../notices", "__init__.py")

# Configure Django for autodoc usage
settings.configure()
Expand All @@ -62,40 +62,40 @@ def get_version(*file_paths):
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'edx_theme',
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.ifconfig',
'sphinx.ext.napoleon'
"edx_theme",
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.ifconfig",
"sphinx.ext.napoleon",
]

# A list of warning types to suppress arbitrary warning messages.
suppress_warnings = [
'image.nonlocal_uri',
"image.nonlocal_uri",
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"

# The encoding of source files.
#
# source_encoding = 'utf-8-sig'

# The top level toctree document.
top_level_doc = 'index'
top_level_doc = "index"

# General information about the project.
project = 'platform-plugin-notices'
project = "platform-plugin-notices"
copyright = edx_theme.COPYRIGHT # pylint: disable=redefined-builtin
author = edx_theme.AUTHOR
project_title = 'platform-plugin-notices'
project_title = "platform-plugin-notices"
documentation_title = "{project_title}".format(project_title=project_title)

# The version info for the project you're documenting, acts as replacement for
Expand Down Expand Up @@ -126,7 +126,7 @@ def get_version(*file_paths):
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# The reST default role (used for this markup: `text`) to use for all
# documents.
Expand All @@ -148,7 +148,7 @@ def get_version(*file_paths):
# show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"

# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
Expand All @@ -165,7 +165,7 @@ def get_version(*file_paths):
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.

html_theme = 'edx_theme'
html_theme = "edx_theme"

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
Expand Down Expand Up @@ -199,7 +199,7 @@ def get_version(*file_paths):
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
Expand Down Expand Up @@ -279,23 +279,20 @@ def get_version(*file_paths):
# html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = '{project_name}doc'.format(project_name=project)
htmlhelp_basename = "{project_name}doc".format(project_name=project)

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',

# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
Expand All @@ -304,10 +301,9 @@ def get_version(*file_paths):
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_target = '{project}.tex'.format(project=project)
latex_target = "{project}.tex".format(project=project)
latex_documents = [
(top_level_doc, latex_target, documentation_title,
author, 'manual'),
(top_level_doc, latex_target, documentation_title, author, "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -347,10 +343,7 @@ def get_version(*file_paths):

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(top_level_doc, project_title, documentation_title,
[author], 1)
]
man_pages = [(top_level_doc, project_title, documentation_title, [author], 1)]

# If true, show URL addresses after external links.
#
Expand All @@ -363,9 +356,15 @@ def get_version(*file_paths):
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(top_level_doc, project_title, documentation_title,
author, project_title, 'An edx-platform plugin which manages notices that must be acknowledged',
'Miscellaneous'),
(
top_level_doc,
project_title,
documentation_title,
author,
project_title,
"An edx-platform plugin which manages notices that must be acknowledged",
"Miscellaneous",
),
]

# Documents to append as an appendix to all manuals.
Expand Down Expand Up @@ -439,7 +438,7 @@ def get_version(*file_paths):
# epub_post_files = []

# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
epub_exclude_files = ["search.html"]

# The depth of the table of contents in toc.ncx.
#
Expand Down Expand Up @@ -472,9 +471,9 @@ def get_version(*file_paths):

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
'python': ('https://docs.python.org/3.6', None),
'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
'model_utils': ('https://django-model-utils.readthedocs.io/en/latest/', None),
"python": ("https://docs.python.org/3.6", None),
"django": ("https://docs.djangoproject.com/en/1.11/", "https://docs.djangoproject.com/en/1.11/_objects/"),
"model_utils": ("https://django-model-utils.readthedocs.io/en/latest/", None),
}


Expand All @@ -486,17 +485,24 @@ def on_init(app): # pylint: disable=unused-argument
avoid checking in the generated reStructuredText files.
"""
docs_path = os.path.abspath(os.path.dirname(__file__))
root_path = os.path.abspath(os.path.join(docs_path, '..'))
apidoc_path = 'sphinx-apidoc'
if hasattr(sys, 'real_prefix'): # Check to see if we are in a virtualenv
root_path = os.path.abspath(os.path.join(docs_path, ".."))
apidoc_path = "sphinx-apidoc"
if hasattr(sys, "real_prefix"): # Check to see if we are in a virtualenv
# If we are, assemble the path manually
bin_path = os.path.abspath(os.path.join(sys.prefix, 'bin'))
bin_path = os.path.abspath(os.path.join(sys.prefix, "bin"))
apidoc_path = os.path.join(bin_path, apidoc_path)
check_call([apidoc_path, '-o', docs_path, os.path.join(root_path, 'notices'),
os.path.join(root_path, 'notices/migrations')])
check_call(
[
apidoc_path,
"-o",
docs_path,
os.path.join(root_path, "notices"),
os.path.join(root_path, "notices/migrations"),
]
)


def setup(app):
"""Sphinx extension: run sphinx-apidoc."""
event = 'builder-inited'
event = "builder-inited"
app.connect(event, on_init)
4 changes: 2 additions & 2 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

PWD = os.path.abspath(os.path.dirname(__file__))

if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_settings')
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_settings")
sys.path.append(PWD)
try:
from django.core.management import execute_from_command_line
Expand Down
4 changes: 2 additions & 2 deletions notices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
An edx-platform plugin which manages notices that must be acknowledged.
"""

__version__ = '0.1.0'
__version__ = "0.1.0"

default_app_config = 'notices.apps.NoticesConfig' # pylint: disable=invalid-name
default_app_config = "notices.apps.NoticesConfig" # pylint: disable=invalid-name
2 changes: 1 addition & 1 deletion notices/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ class NoticesConfig(AppConfig):
"production": {"relative_path": "settings.production"},
"common": {"relative_path": "settings.common"},
}
}
},
}
1 change: 1 addition & 0 deletions notices/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class AcknowledgmentResponseTypes(str, Enum):
"""
Options for the response_type field of a AcknowledgedNotice.
"""

CONFIRMED = "confirmed"
DISMISSED = "dismissed"

Expand Down
5 changes: 4 additions & 1 deletion notices/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class TranslatedNoticeContent(TimeStampedModel):
.. no_pii:
"""

notice = models.ForeignKey(Notice, on_delete=models.CASCADE, related_name="translated_notice_content")
language_code = models.CharField(max_length=10, help_text="The IETF BCP 47 language code for this translation")
html_content = models.TextField(
Expand All @@ -58,7 +59,7 @@ class Meta:
"""Model metadata."""

app_label = "notices"
unique_together = ['notice', 'language_code']
unique_together = ["notice", "language_code"]

def save(self, *args, **kwargs):
"""Save method override to remove unsafe tags from html_content first."""
Expand All @@ -74,6 +75,7 @@ class AcknowledgedNotice(TimeStampedModel):
.. no_pii:
"""

RESPONSE_TYPE_CHOICES = [
(AcknowledgmentResponseTypes.CONFIRMED.value, "Confirmed"),
(AcknowledgmentResponseTypes.DISMISSED.value, "Dismissed"),
Expand All @@ -85,6 +87,7 @@ class AcknowledgedNotice(TimeStampedModel):

class Meta:
"""Model metadata."""

app_label = "notices"

def __str__(self):
Expand Down
6 changes: 4 additions & 2 deletions notices/rest_api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@

class TranslateNoticeContentSerializer(serializers.ModelSerializer):
"""Serializer for translated notice content"""

class Meta:
model = TranslatedNoticeContent
fields = ('language_code', 'html_content')
fields = ("language_code", "html_content")


class NoticeSerializer(serializers.ModelSerializer):
"""Serializer for notice"""

translated_notice_content = TranslateNoticeContentSerializer(many=True, read_only=True)

class Meta:
model = Notice
fields = ('id', 'name', 'translated_notice_content')
fields = ("id", "name", "translated_notice_content")
2 changes: 1 addition & 1 deletion notices/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@

urlpatterns = [
url(r"unacknowledged", views.ListUnacknowledgedNotices.as_view(), name="unacknowledged_notices"),
url(r"acknowledge", views.AcknowledgeNotice.as_view(), name="acknowledge_notice")
url(r"acknowledge", views.AcknowledgeNotice.as_view(), name="acknowledge_notice"),
]
26 changes: 16 additions & 10 deletions notices/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ class ListUnacknowledgedNotices(APIView):
]
}
"""
authentication_classes = (JwtAuthentication, SessionAuthentication,)

authentication_classes = (
JwtAuthentication,
SessionAuthentication,
)
permission_classes = (permissions.IsAuthenticated,)

def get(self, request):
Expand Down Expand Up @@ -75,7 +79,11 @@ class AcknowledgeNotice(APIView):
POST /api/notices/v1/acknowledge
post data: {"notice_id": 10, "acknowledgment_type": "confirmed"}
"""
authentication_classes = (JwtAuthentication, SessionAuthentication,)

authentication_classes = (
JwtAuthentication,
SessionAuthentication,
)
permission_classes = (permissions.IsAuthenticated,)

def post(self, request):
Expand All @@ -86,23 +94,21 @@ def post(self, request):
acknowledgment_type = request.data.get("acknowledgment_type")

if not notice_id:
raise ValidationError({'notice_id': "notice_id field required"})
raise ValidationError({"notice_id": "notice_id field required"})

if not AcknowledgmentResponseTypes.includes_value(acknowledgment_type):
valid_types = [e.value for e in AcknowledgmentResponseTypes]
raise ValidationError({
'acknowledgment_type': f"acknowledgment_type must be one of the following: {valid_types}"
})
raise ValidationError(
{"acknowledgment_type": f"acknowledgment_type must be one of the following: {valid_types}"}
)

try:
notice = Notice.objects.get(id=notice_id, active=True)
except Notice.DoesNotExist as exc:
raise ValidationError({'notice_id': "notice_id field does not match an existing active notice"}) from exc
raise ValidationError({"notice_id": "notice_id field does not match an existing active notice"}) from exc

AcknowledgedNotice.objects.update_or_create(
user=request.user,
notice=notice,
defaults={"response_type": acknowledgment_type}
user=request.user, notice=notice, defaults={"response_type": acknowledgment_type}
)
# Since this is just an acknowledgment API, we can just return a 204 without any response data.
return Response(status=HTTP_204_NO_CONTENT)
Loading

0 comments on commit d97f60a

Please sign in to comment.