chore: reformat/add type hints

This commit is contained in:
Ethan Nguyen 2021-04-19 21:30:14 -04:00
parent 97b1bb0f34
commit 13db62b837
No known key found for this signature in database
GPG Key ID: B4CA5339AF911920
16 changed files with 120 additions and 36 deletions

3
.flake8 Normal file
View File

@ -0,0 +1,3 @@
[flake8]
max-line-length = 100
exclude=*/media/*,*/migrations/*,secret*

11
.isort.cfg Normal file
View File

@ -0,0 +1,11 @@
[settings]
line_length=100
multi_line_output=3
include_trailing_comma=true
skip=tjdests/media
skip_glob=*/migrations/*,secret*
known_django=django
known_tjdests=tjdests
default_section=THIRDPARTY
sections=FUTURE,STDLIB,THIRDPARTY,DJANGO,TJDESTS,LOCALFOLDER

9
mypy.ini Normal file
View File

@ -0,0 +1,9 @@
[mypy]
ignore_missing_imports = True
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = tjdests.settings
[mypy-tjdests.apps.*.migrations.*]
ignore_errors = True

15
pylintrc Normal file
View File

@ -0,0 +1,15 @@
[MASTER]
init-hook=import sys; sys.path.insert(0, 'tjdests/apps')
load-plugins=pylint_django
[MAIN]
disable=missing-docstring,no-else-return,no-else-raise,bad-continuation,duplicate-code,too-many-branches,too-many-nested-blocks,too-many-locals,too-many-statements,too-many-public-methods
max-line-length=100
ignore=media,migrations
ignore-patterns=secret*
django-settings-module=tjdests.settings
good-names=i,j,j,ex,ch,fd,T,_
const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$

35
scripts/check.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
# MIT License
#
# Copyright (c) 2017 The TJ Director Development Team
# Copyright (c) 2019 The TJHSST Director 4.0 Development Team & Contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cd "$(dirname -- "$(dirname -- "$(readlink -f "$0")")")"
for cmd in flake8 isort mypy pylint; do
if [[ ! -x "$(which "$cmd")" ]]; then
echo "Could not find $cmd. Please make sure that flake8, isort, mypy, and pylint are all installed."
exit 1
fi
done
flake8 tjdests && isort --check tjdests && mypy tjdests && pylint tjdests

View File

@ -1,5 +1,6 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.contrib.auth import password_validation
@ -14,22 +15,22 @@ class TOSForm(forms.Form):
accept_tos = forms.BooleanField(
required=True,
label="I accept the terms of the GNU Affero General Public License as displayed above,"
" and I understand that the terms that provide this software WITHOUT ANY WARRANTY;"
" without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
label="I accept the terms of the GNU Affero General Public License as displayed above, "
"and I understand that the terms that provide this software WITHOUT ANY WARRANTY; "
"without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
)
password = forms.CharField(widget=forms.PasswordInput, required=True)
password_confirm = forms.CharField(
widget=forms.PasswordInput, required=True)
password_confirm = forms.CharField(widget=forms.PasswordInput, required=True)
understand_no_reset = forms.BooleanField(
required=True,
label="I understand that there is NO PASSWORD RESET functionality once I no longer have access to Ion.",
label="I understand that there is NO PASSWORD RESET "
"functionality once I no longer have access to Ion.",
)
def clean(self):
cleaned_data = super(TOSForm, self).clean()
cleaned_data = super().clean()
password1 = cleaned_data.get("password")
password2 = cleaned_data.get("password_confirm")

View File

@ -25,8 +25,7 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
("password", models.CharField(
max_length=128, verbose_name="password")),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
@ -101,8 +100,7 @@ class Migration(migrations.Migration):
("is_senior", models.BooleanField(default=False)),
(
"publish_data",
models.BooleanField(
default=False, verbose_name="Publish my data"),
models.BooleanField(default=False, verbose_name="Publish my data"),
),
("biography", models.TextField(blank=True)),
],

View File

@ -1,7 +1,7 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from ..destinations.models import Decision, TestScore
from ..destinations.models import Decision
class User(AbstractUser):

View File

@ -1,14 +1,14 @@
from django.conf import settings
from social_core.backends.oauth import BaseOAuth2
from django.conf import settings
class IonOauth2(BaseOAuth2):
class IonOauth2(BaseOAuth2): # pylint: disable=abstract-method
name = "ion"
AUTHORIZATION_URL = "https://ion.tjhsst.edu/oauth/authorize"
ACCESS_TOKEN_URL = "https://ion.tjhsst.edu/oauth/token"
ACCESS_TOKEN_METHOD = "POST"
EXTRA_DATA = [("refresh_token", "refresh_token", True),
("expires_in", "expires")]
EXTRA_DATA = [("refresh_token", "refresh_token", True), ("expires_in", "expires")]
def get_scope(self):
return ["read"]

View File

@ -6,7 +6,6 @@ from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from tjdests.apps.authentication.decorators import require_accept_tos
from tjdests.apps.authentication.forms import TOSForm

View File

@ -4,8 +4,7 @@ from django.db import models
class College(models.Model):
"""Represents a college."""
ceeb_code = models.CharField(
max_length=10, null=False, verbose_name="CEEB Code")
ceeb_code = models.CharField(max_length=10, null=False, verbose_name="CEEB Code")
name = models.CharField(max_length=250, null=False, blank=False)
location = models.CharField(max_length=250, null=False, blank=False)
@ -58,12 +57,14 @@ class Decision(models.Model):
(DENY, "Denied"),
]
admission_status = models.CharField(
max_length=20, choices=ADMIT_TYPE_CHOICES)
admission_status = models.CharField(max_length=20, choices=ADMIT_TYPE_CHOICES)
college = models.ForeignKey(College, on_delete=models.CASCADE)
def __str__(self):
return f"{self.college.name} - {self.get_decision_type_display()}: {self.get_admission_status_display()}"
return (
f"{self.college.name} - {self.get_decision_type_display()}: "
f"{self.get_admission_status_display()}"
)
class TestScore(models.Model):

View File

@ -7,7 +7,9 @@ from ..authentication.models import User
from .models import College, Decision
class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
class StudentDestinationListView(
LoginRequiredMixin, UserPassesTestMixin, ListView
): # pylint: disable=too-many-ancestors
model = User
paginate_by = 20
@ -23,9 +25,10 @@ class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListVi
return queryset
def get_context_data(self, *, object_list=None, **kwargs):
context = super(StudentDestinationListView,
self).get_context_data(**kwargs)
def get_context_data(
self, *, object_list=None, **kwargs
): # pylint: disable=unused-argument
context = super().get_context_data(**kwargs)
college_id = self.request.GET.get("college", None)
if college_id is not None:
@ -39,7 +42,9 @@ class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListVi
template_name = "destinations/student_list.html"
class CollegeDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
class CollegeDestinationListView(
LoginRequiredMixin, UserPassesTestMixin, ListView
): # pylint: disable=too-many-ancestors
model = College
paginate_by = 20
queryset = (

View File

@ -1,5 +1,6 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from tjdests.apps.authentication.models import User

View File

@ -10,14 +10,18 @@ from django.views.generic import CreateView, DeleteView, UpdateView
from tjdests.apps.authentication.decorators import require_accept_tos
from tjdests.apps.destinations.models import Decision, TestScore
from .forms import DecisionForm, ProfilePublishForm
from ..authentication.models import User
from .forms import ProfilePublishForm
@login_required
@require_accept_tos
def profile_view(request: HttpRequest):
test_scores = TestScore.objects.filter(user=request.user)
decisions = Decision.objects.filter(user=request.user)
assert request.user is User
# mypy is bad.
test_scores = TestScore.objects.filter(user=request.user) # type: ignore
decisions = Decision.objects.filter(user=request.user) # type: ignore
# A POST request would mean that the user is saving their profile publication status
if request.method == "POST":

View File

@ -12,8 +12,9 @@ https://docs.djangoproject.com/en/3.2/ref/settings/
import logging
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
from typing import List
BASE_DIR = Path(__file__).resolve().parent.parent
@ -26,7 +27,7 @@ SECRET_KEY = "django-insecure-7nju0o%j&gz7&v^05iuq*tn$_iwvtjh1cq26@is(u2d4snkum5
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS: List[str] = []
# Application definition
@ -157,7 +158,6 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5"
try:
from .secret import *
from .secret import * # noqa # pylint: disable=unused-import
except ImportError:
logging.warning("Error importing secret.py")
pass

View File

@ -1,5 +1,7 @@
# Deployed senior graduation year
# e.g. if deploying in spring 2021, then 2021
from typing import List
SENIOR_GRAD_YEAR = 2021
# Branding name
@ -7,7 +9,7 @@ BRANDING_NAME = "TJ Destinations"
# DEBUG and authorized hosts
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS: List[str] = []
# secret
SECRET_KEY = "supersecret"
@ -17,5 +19,5 @@ SOCIAL_AUTH_ION_KEY = "ionkey"
SOCIAL_AUTH_ION_SECRET = "ionsecret"
# Message blast - treated as HTML safe text
# type: str
# type is str
GLOBAL_MESSAGE = None