diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 0000000..1cb2e78 --- /dev/null +++ b/scripts/format.sh @@ -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 black autopep8 isort; do + if [[ ! -x "$(which "$cmd")" ]]; then + echo "Could not find $cmd. Please make sure that black, autopep8, and isort are all installed." + exit 1 + fi +done + +black tjdests && autopep8 --in-place --recursive tjdests && isort tjdests diff --git a/tjdests/apps/authentication/decorators.py b/tjdests/apps/authentication/decorators.py index 69dfa3c..583c0af 100644 --- a/tjdests/apps/authentication/decorators.py +++ b/tjdests/apps/authentication/decorators.py @@ -12,4 +12,4 @@ def require_accept_tos(func): return func(request, *args, **kwargs) - return wrapper \ No newline at end of file + return wrapper diff --git a/tjdests/apps/authentication/forms.py b/tjdests/apps/authentication/forms.py index 55920b8..f4d7400 100644 --- a/tjdests/apps/authentication/forms.py +++ b/tjdests/apps/authentication/forms.py @@ -1,47 +1,69 @@ -from django import forms -from django.contrib.auth import password_validation - from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit +from django import forms +from django.contrib.auth import password_validation class TOSForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' + self.helper.form_method = "post" - self.helper.add_input(Submit('submit', 'Submit')) + self.helper.add_input(Submit("submit", "Submit")) - 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.") + 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.", + ) 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.") + 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.", + ) def clean(self): cleaned_data = super(TOSForm, self).clean() - password1 = cleaned_data.get('password') - password2 = cleaned_data.get('password_confirm') + password1 = cleaned_data.get("password") + password2 = cleaned_data.get("password_confirm") if password1 and password1 != password2: - raise forms.ValidationError({"password": ["The two passwords do not match.",]}) + raise forms.ValidationError( + { + "password": [ + "The two passwords do not match.", + ] + } + ) # Validate checkboxes checked accept_tos = cleaned_data.get("accept_tos") understand_no_reset = cleaned_data.get("understand_no_reset") if not accept_tos: - raise forms.ValidationError({"accept_tos": ["You must accept the license terms to continue.",]}) + raise forms.ValidationError( + { + "accept_tos": [ + "You must accept the license terms to continue.", + ] + } + ) if not understand_no_reset: - raise forms.ValidationError({"understand_no_reset": ["You must acknowledge that there is no password reset to continue.",]}) + raise forms.ValidationError( + { + "understand_no_reset": [ + "You must acknowledge that there is no password reset to continue.", + ] + } + ) # Validate the password for complexity, etc. validators = password_validation.get_default_password_validators() password_validation.validate_password(password1, None, validators) - - diff --git a/tjdests/apps/authentication/migrations/0001_initial.py b/tjdests/apps/authentication/migrations/0001_initial.py index b99525a..03e4b3c 100644 --- a/tjdests/apps/authentication/migrations/0001_initial.py +++ b/tjdests/apps/authentication/migrations/0001_initial.py @@ -2,45 +2,117 @@ import django.contrib.auth.models import django.contrib.auth.validators -from django.db import migrations, models import django.utils.timezone +from django.db import migrations, models class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('accepted_terms', models.BooleanField(default=False)), - ('graduation_year', models.PositiveSmallIntegerField(null=True)), - ('is_senior', models.BooleanField(default=False)), - ('publish_data', models.BooleanField(default=False, verbose_name='Publish my data')), - ('biography', models.TextField(blank=True)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField( + max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=254, verbose_name="email address" + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ("accepted_terms", models.BooleanField(default=False)), + ("graduation_year", models.PositiveSmallIntegerField(null=True)), + ("is_senior", models.BooleanField(default=False)), + ( + "publish_data", + models.BooleanField( + default=False, verbose_name="Publish my data"), + ), + ("biography", models.TextField(blank=True)), ], options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), ] diff --git a/tjdests/apps/authentication/migrations/0002_initial.py b/tjdests/apps/authentication/migrations/0002_initial.py index 48a28b8..d8555cf 100644 --- a/tjdests/apps/authentication/migrations/0002_initial.py +++ b/tjdests/apps/authentication/migrations/0002_initial.py @@ -1,7 +1,7 @@ # Generated by Django 3.2 on 2021-04-19 15:03 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): @@ -9,25 +9,46 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ('authentication', '0001_initial'), - ('destinations', '0001_initial'), + ("auth", "0012_alter_user_first_name_max_length"), + ("authentication", "0001_initial"), + ("destinations", "0001_initial"), ] operations = [ migrations.AddField( - model_name='user', - name='attending_decision', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='attending_college', to='destinations.decision', verbose_name='College attending'), + model_name="user", + name="attending_decision", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="attending_college", + to="destinations.decision", + verbose_name="College attending", + ), ), migrations.AddField( - model_name='user', - name='groups', - field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + model_name="user", + name="groups", + field=models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), ), migrations.AddField( - model_name='user', - name='user_permissions', - field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + model_name="user", + name="user_permissions", + field=models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), ), ] diff --git a/tjdests/apps/authentication/migrations/0003_alter_user_publish_data.py b/tjdests/apps/authentication/migrations/0003_alter_user_publish_data.py index 98038fe..37d940b 100644 --- a/tjdests/apps/authentication/migrations/0003_alter_user_publish_data.py +++ b/tjdests/apps/authentication/migrations/0003_alter_user_publish_data.py @@ -6,13 +6,17 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('authentication', '0002_initial'), + ("authentication", "0002_initial"), ] operations = [ migrations.AlterField( - model_name='user', - name='publish_data', - field=models.BooleanField(default=False, help_text='Unless this is set, your data will not appear publicly.', verbose_name='Publish my data'), + model_name="user", + name="publish_data", + field=models.BooleanField( + default=False, + help_text="Unless this is set, your data will not appear publicly.", + verbose_name="Publish my data", + ), ), ] diff --git a/tjdests/apps/authentication/migrations/0004_auto_20210419_1713.py b/tjdests/apps/authentication/migrations/0004_auto_20210419_1713.py index a351a05..ee778ce 100644 --- a/tjdests/apps/authentication/migrations/0004_auto_20210419_1713.py +++ b/tjdests/apps/authentication/migrations/0004_auto_20210419_1713.py @@ -1,25 +1,33 @@ # Generated by Django 3.2 on 2021-04-19 17:13 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('destinations', '0005_alter_decision_admission_status'), - ('authentication', '0003_alter_user_publish_data'), + ("destinations", "0005_alter_decision_admission_status"), + ("authentication", "0003_alter_user_publish_data"), ] operations = [ migrations.AddField( - model_name='user', - name='is_student', + model_name="user", + name="is_student", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='user', - name='attending_decision', - field=models.ForeignKey(blank=True, help_text="Can't see your college? Make sure you've added a decision with an admit status.", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='attending_college', to='destinations.decision', verbose_name='College attending'), + model_name="user", + name="attending_decision", + field=models.ForeignKey( + blank=True, + help_text="Can't see your college? Make sure you've added a decision with an admit status.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="attending_college", + to="destinations.decision", + verbose_name="College attending", + ), ), ] diff --git a/tjdests/apps/authentication/models.py b/tjdests/apps/authentication/models.py index bc7134e..9030c56 100644 --- a/tjdests/apps/authentication/models.py +++ b/tjdests/apps/authentication/models.py @@ -1,8 +1,9 @@ -from django.db import models from django.contrib.auth.models import AbstractUser +from django.db import models from ..destinations.models import Decision, TestScore + class User(AbstractUser): accepted_terms = models.BooleanField(default=False) graduation_year = models.PositiveSmallIntegerField(null=True) @@ -11,12 +12,22 @@ class User(AbstractUser): is_student = models.BooleanField(default=False) # The rest are used only if a senior - publish_data = models.BooleanField(default=False, verbose_name="Publish my data", help_text="Unless this is set, your data will not appear publicly.") + publish_data = models.BooleanField( + default=False, + verbose_name="Publish my data", + help_text="Unless this is set, your data will not appear publicly.", + ) biography = models.TextField(blank=True) - attending_decision = models.ForeignKey(Decision, on_delete=models.SET_NULL, null=True, blank=True, - verbose_name="College attending", related_name="attending_college", - help_text="Can't see your college? Make sure you've added a decision with an admit status.") + attending_decision = models.ForeignKey( + Decision, + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name="College attending", + related_name="attending_college", + help_text="Can't see your college? Make sure you've added a decision with an admit status.", + ) def __str__(self): return f"{self.first_name} {self.last_name}" diff --git a/tjdests/apps/authentication/oauth.py b/tjdests/apps/authentication/oauth.py index d3523c3..74b2fa8 100644 --- a/tjdests/apps/authentication/oauth.py +++ b/tjdests/apps/authentication/oauth.py @@ -7,14 +7,16 @@ class IonOauth2(BaseOAuth2): 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"] def get_user_details(self, response): profile = self.get_json( - "https://ion.tjhsst.edu/api/profile", params={"access_token": response["access_token"]} + "https://ion.tjhsst.edu/api/profile", + params={"access_token": response["access_token"]}, ) # fields used to populate/update User model @@ -31,8 +33,8 @@ class IonOauth2(BaseOAuth2): "is_student": profile["is_student"], "is_teacher": profile["is_teacher"], "graduation_year": profile["graduation_year"], - "is_senior": int(profile["graduation_year"]) == settings.SENIOR_GRAD_YEAR + "is_senior": int(profile["graduation_year"]) == settings.SENIOR_GRAD_YEAR, } def get_user_id(self, details, response): - return details["id"] \ No newline at end of file + return details["id"] diff --git a/tjdests/apps/authentication/urls.py b/tjdests/apps/authentication/urls.py index 8f6b535..e1a6873 100644 --- a/tjdests/apps/authentication/urls.py +++ b/tjdests/apps/authentication/urls.py @@ -1,5 +1,6 @@ from django.contrib.auth.views import LogoutView from django.urls import path + from . import views app_name = "authentication" @@ -9,4 +10,4 @@ urlpatterns = [ path("login", views.LoginViewCustom.as_view(), name="login"), path("logout", LogoutView.as_view(), name="logout"), path("tos", views.accept_tos_view, name="tos"), -] \ No newline at end of file +] diff --git a/tjdests/apps/authentication/views.py b/tjdests/apps/authentication/views.py index d76dd11..6f2807a 100644 --- a/tjdests/apps/authentication/views.py +++ b/tjdests/apps/authentication/views.py @@ -1,9 +1,9 @@ +from django.contrib import messages from django.contrib.auth import login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.views import LoginView -from django.contrib import messages from django.http import HttpRequest, HttpResponse -from django.shortcuts import render, redirect +from django.shortcuts import redirect, render from django.urls import reverse from tjdests.apps.authentication.decorators import require_accept_tos @@ -13,6 +13,7 @@ from tjdests.apps.authentication.forms import TOSForm def index_view(request: HttpRequest) -> HttpResponse: return render(request, "authentication/index.html") + @login_required def accept_tos_view(request: HttpRequest) -> HttpResponse: assert request.user.is_authenticated @@ -33,7 +34,11 @@ def accept_tos_view(request: HttpRequest) -> HttpResponse: request.user.set_password(form.cleaned_data.get("password")) request.user.save() - login(request, request.user, backend='django.contrib.auth.backends.ModelBackend') + login( + request, + request.user, + backend="django.contrib.auth.backends.ModelBackend", + ) messages.success(request, "You have logged in.") @@ -45,6 +50,6 @@ def accept_tos_view(request: HttpRequest) -> HttpResponse: return render(request, "authentication/accept_tos.html", context=context) + class LoginViewCustom(LoginView): template_name = "authentication/login.html" - diff --git a/tjdests/apps/context_processors.py b/tjdests/apps/context_processors.py index eaa9498..e471940 100644 --- a/tjdests/apps/context_processors.py +++ b/tjdests/apps/context_processors.py @@ -1,4 +1,5 @@ from django.conf import settings + def settings_renderer(request): return {"settings": settings} diff --git a/tjdests/apps/destinations/admin.py b/tjdests/apps/destinations/admin.py index c68d2b8..d93198b 100644 --- a/tjdests/apps/destinations/admin.py +++ b/tjdests/apps/destinations/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import College, TestScore, Decision +from .models import College, Decision, TestScore admin.site.register(College) admin.site.register(TestScore) diff --git a/tjdests/apps/destinations/management/commands/import_ceeb.py b/tjdests/apps/destinations/management/commands/import_ceeb.py index 7cd0cce..e6a13f6 100644 --- a/tjdests/apps/destinations/management/commands/import_ceeb.py +++ b/tjdests/apps/destinations/management/commands/import_ceeb.py @@ -5,6 +5,7 @@ from django.core.management.base import BaseCommand from ...models import College + class Command(BaseCommand): help = "Imports a CSV of CEEB codes as colleges" @@ -20,9 +21,21 @@ class Command(BaseCommand): reader = csv.DictReader(file) for line in reader: - result = College.objects.update_or_create(ceeb_code=line["CEEB"], defaults={"name": line["College Name"], "location": f"{line['City']}, {line['State']}"}) + result = College.objects.update_or_create( + ceeb_code=line["CEEB"], + defaults={ + "name": line["College Name"], + "location": f"{line['City']}, {line['State']}", + }, + ) if result[1]: - self.stdout.write(f"Added university {result[0].name}.", style_func=self.style.SUCCESS) + self.stdout.write( + f"Added university {result[0].name}.", + style_func=self.style.SUCCESS, + ) else: - self.stdout.write(f"Did not update university {result[0].name}.", style_func=self.style.WARNING) + self.stdout.write( + f"Did not update university {result[0].name}.", + style_func=self.style.WARNING, + ) diff --git a/tjdests/apps/destinations/migrations/0001_initial.py b/tjdests/apps/destinations/migrations/0001_initial.py index c75fd69..c014683 100644 --- a/tjdests/apps/destinations/migrations/0001_initial.py +++ b/tjdests/apps/destinations/migrations/0001_initial.py @@ -1,8 +1,8 @@ # Generated by Django 3.2 on 2021-04-19 15:03 +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -15,30 +15,165 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='College', + name="College", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ceeb_code', models.PositiveSmallIntegerField(verbose_name='CEEB Code')), - ('name', models.CharField(max_length=250)), - ('location', models.CharField(max_length=250)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "ceeb_code", + models.PositiveSmallIntegerField(verbose_name="CEEB Code"), + ), + ("name", models.CharField(max_length=250)), + ("location", models.CharField(max_length=250)), ], ), migrations.CreateModel( - name='TestScore', + name="TestScore", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('exam_type', models.CharField(choices=[('ACT_ENGL', 'ACT English (Grammar)'), ('ACT_MATH', 'ACT Math'), ('ACT_READ', 'ACT Reading'), ('ACT_SCI', 'ACT Science'), ('ACT_COMP', 'ACT Composite'), ('SAT_EBRW', 'SAT Verbal'), ('SAT_MATH', 'SAT Math'), ('SAT_TOTAL', 'SAT Total'), ('SAT2_MATH1', 'SAT Subject Test Math 1'), ('SAT2_MATH2', 'SAT Subject Test Math 2'), ('SAT2_BIO', 'SAT Subject Test Biology'), ('SAT2_CHEM', 'SAT Subject Test Chemistry'), ('SAT2_PHYS', 'SAT Subject Test Physics'), ('SAT2_ENGL', 'SAT Subject Test English'), ('SAT2_USH', 'SAT Subject Test U.S. History'), ('SAT2_WH', 'SAT Subject Test World History'), ('SAT2_ES', 'SAT Subject Test Spanish'), ('SAT2_ESL', 'SAT Subject Test Spanish with Listening'), ('SAT2_FR', 'SAT Subject Test French'), ('SAT2_FRL', 'SAT Subject Test French with Listening'), ('SAT2_ZHL', 'SAT Subject Test Chinese with Listening'), ('SAT2_IT', 'SAT Subject Test Italian'), ('SAT2_DE', 'SAT Subject Test German'), ('SAT2_DEL', 'SAT Subject Test German with Listening'), ('SAT2_HE', 'SAT Subject Test Modern Hebrew'), ('SAT2_LA', 'SAT Subject Test Latin'), ('SAT2_JAL', 'SAT Subject Test Japanese with Listening'), ('SAT2_KOL', 'SAT Subject Test Korean with Listening'), ('AP_RSRCH', 'AP Research'), ('AP_SMNR', 'AP Seminar'), ('AP_ART2D', 'AP Art and Design: 2-D Design'), ('AP_ART3D', 'AP Art and Design: 3-D Design'), ('AP_ARTDRAW', 'AP Art and Design: Drawing'), ('AP_ARTHIST', 'AP Art History'), ('AP_BIO', 'AP Biology'), ('AP_CALCAB', 'AP Calculus AB'), ('AP_CALCBC', 'AP Calculus BC'), ('AP_CHEM', 'AP Chemistry'), ('AP_ZHLANG', 'AP Chinese Language and Culture'), ('AP_CSA', 'AP Computer Science A'), ('AP_CSP', 'AP Computer Science Principles'), ('AP_ENLANG', 'AP English Language and Composition'), ('AP_ENLIT', 'AP English Literature and Composition'), ('AP_ENVSCI', 'AP Environmental Science'), ('AP_EUROHIST', 'AP European History'), ('AP_FRLANG', 'AP French Language and Culture'), ('AP_DELANG', 'AP German Language and Culture'), ('AP_GOVCOMP', 'AP Comparative Government and Politics'), ('AP_GOVUS', 'AP U.S. Government and Politics'), ('AP_HUG', 'AP Human Geography'), ('AP_ITLANG', 'AP Italian Language and Culture'), ('AP_JALANG', 'AP Japanese Language and Culture'), ('AP_LATIN', 'AP Latin'), ('AP_MACRO', 'AP Macroeconomics'), ('AP_MICRO', 'AP Microeconomics'), ('AP_MUSTHRY', 'AP Music Theory'), ('AP_PHYSICS1', 'AP Physics 1: Algebra-Based'), ('AP_PHYSICS2', 'AP Physics 2: Algebra-Based'), ('AP_PHYSICSCEM', 'AP Physics C: Electricity and Magnetism'), ('AP_PHYSICSCM', 'AP Physics C: Mechanics'), ('AP_PSYCH', 'AP Psychology'), ('AP_ESLANG', 'AP Spanish Language and Culture'), ('AP_ESLIT', 'AP Spanish Literature and Culture'), ('AP_STAT', 'AP Statistics'), ('AP_USH', 'AP US History'), ('AP_WHM', 'AP World History: Modern')], max_length=20)), - ('exam_score', models.PositiveSmallIntegerField()), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "exam_type", + models.CharField( + choices=[ + ("ACT_ENGL", "ACT English (Grammar)"), + ("ACT_MATH", "ACT Math"), + ("ACT_READ", "ACT Reading"), + ("ACT_SCI", "ACT Science"), + ("ACT_COMP", "ACT Composite"), + ("SAT_EBRW", "SAT Verbal"), + ("SAT_MATH", "SAT Math"), + ("SAT_TOTAL", "SAT Total"), + ("SAT2_MATH1", "SAT Subject Test Math 1"), + ("SAT2_MATH2", "SAT Subject Test Math 2"), + ("SAT2_BIO", "SAT Subject Test Biology"), + ("SAT2_CHEM", "SAT Subject Test Chemistry"), + ("SAT2_PHYS", "SAT Subject Test Physics"), + ("SAT2_ENGL", "SAT Subject Test English"), + ("SAT2_USH", "SAT Subject Test U.S. History"), + ("SAT2_WH", "SAT Subject Test World History"), + ("SAT2_ES", "SAT Subject Test Spanish"), + ("SAT2_ESL", "SAT Subject Test Spanish with Listening"), + ("SAT2_FR", "SAT Subject Test French"), + ("SAT2_FRL", "SAT Subject Test French with Listening"), + ("SAT2_ZHL", "SAT Subject Test Chinese with Listening"), + ("SAT2_IT", "SAT Subject Test Italian"), + ("SAT2_DE", "SAT Subject Test German"), + ("SAT2_DEL", "SAT Subject Test German with Listening"), + ("SAT2_HE", "SAT Subject Test Modern Hebrew"), + ("SAT2_LA", "SAT Subject Test Latin"), + ("SAT2_JAL", "SAT Subject Test Japanese with Listening"), + ("SAT2_KOL", "SAT Subject Test Korean with Listening"), + ("AP_RSRCH", "AP Research"), + ("AP_SMNR", "AP Seminar"), + ("AP_ART2D", "AP Art and Design: 2-D Design"), + ("AP_ART3D", "AP Art and Design: 3-D Design"), + ("AP_ARTDRAW", "AP Art and Design: Drawing"), + ("AP_ARTHIST", "AP Art History"), + ("AP_BIO", "AP Biology"), + ("AP_CALCAB", "AP Calculus AB"), + ("AP_CALCBC", "AP Calculus BC"), + ("AP_CHEM", "AP Chemistry"), + ("AP_ZHLANG", "AP Chinese Language and Culture"), + ("AP_CSA", "AP Computer Science A"), + ("AP_CSP", "AP Computer Science Principles"), + ("AP_ENLANG", "AP English Language and Composition"), + ("AP_ENLIT", "AP English Literature and Composition"), + ("AP_ENVSCI", "AP Environmental Science"), + ("AP_EUROHIST", "AP European History"), + ("AP_FRLANG", "AP French Language and Culture"), + ("AP_DELANG", "AP German Language and Culture"), + ("AP_GOVCOMP", "AP Comparative Government and Politics"), + ("AP_GOVUS", "AP U.S. Government and Politics"), + ("AP_HUG", "AP Human Geography"), + ("AP_ITLANG", "AP Italian Language and Culture"), + ("AP_JALANG", "AP Japanese Language and Culture"), + ("AP_LATIN", "AP Latin"), + ("AP_MACRO", "AP Macroeconomics"), + ("AP_MICRO", "AP Microeconomics"), + ("AP_MUSTHRY", "AP Music Theory"), + ("AP_PHYSICS1", "AP Physics 1: Algebra-Based"), + ("AP_PHYSICS2", "AP Physics 2: Algebra-Based"), + ( + "AP_PHYSICSCEM", + "AP Physics C: Electricity and Magnetism", + ), + ("AP_PHYSICSCM", "AP Physics C: Mechanics"), + ("AP_PSYCH", "AP Psychology"), + ("AP_ESLANG", "AP Spanish Language and Culture"), + ("AP_ESLIT", "AP Spanish Literature and Culture"), + ("AP_STAT", "AP Statistics"), + ("AP_USH", "AP US History"), + ("AP_WHM", "AP World History: Modern"), + ], + max_length=20, + ), + ), + ("exam_score", models.PositiveSmallIntegerField()), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='Decision', + name="Decision", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('decision_type', models.CharField(choices=[('ED', 'Early Decision'), ('ED2', 'Early Decision 2'), ('EA', 'Early Action'), ('EA2', 'Early Action 2'), ('RD', 'Regular Decision'), ('RL', 'Rolling')], max_length=20, null=True)), - ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='destinations.college')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "decision_type", + models.CharField( + choices=[ + ("ED", "Early Decision"), + ("ED2", "Early Decision 2"), + ("EA", "Early Action"), + ("EA2", "Early Action 2"), + ("RD", "Regular Decision"), + ("RL", "Rolling"), + ], + max_length=20, + null=True, + ), + ), + ( + "college", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="destinations.college", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/tjdests/apps/destinations/migrations/0002_decision_admitted.py b/tjdests/apps/destinations/migrations/0002_decision_admitted.py index 0e81d52..198f497 100644 --- a/tjdests/apps/destinations/migrations/0002_decision_admitted.py +++ b/tjdests/apps/destinations/migrations/0002_decision_admitted.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('destinations', '0001_initial'), + ("destinations", "0001_initial"), ] operations = [ migrations.AddField( - model_name='decision', - name='admitted', + model_name="decision", + name="admitted", field=models.BooleanField(default=False), ), ] diff --git a/tjdests/apps/destinations/migrations/0003_alter_decision_admitted.py b/tjdests/apps/destinations/migrations/0003_alter_decision_admitted.py index 34d2d3e..ba754fb 100644 --- a/tjdests/apps/destinations/migrations/0003_alter_decision_admitted.py +++ b/tjdests/apps/destinations/migrations/0003_alter_decision_admitted.py @@ -6,13 +6,26 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('destinations', '0002_decision_admitted'), + ("destinations", "0002_decision_admitted"), ] operations = [ migrations.AlterField( - model_name='decision', - name='admitted', - field=models.CharField(choices=[('ADMIT', 'Admitted'), ('WAITLIST', 'Waitlisted'), ('WAITLIST_ADMIT', 'Waitlist-Admitted'), ('WAITLIST_DENY', 'Waitlist-Denied'), ('DEFER', 'Deferred'), ('DEFER_ADMIT', 'Deferred-Admitted'), ('DEFER_DENY', 'Deferred-Denied'), ('DENY', 'Denied')], default='DENY', max_length=20), + model_name="decision", + name="admitted", + field=models.CharField( + choices=[ + ("ADMIT", "Admitted"), + ("WAITLIST", "Waitlisted"), + ("WAITLIST_ADMIT", "Waitlist-Admitted"), + ("WAITLIST_DENY", "Waitlist-Denied"), + ("DEFER", "Deferred"), + ("DEFER_ADMIT", "Deferred-Admitted"), + ("DEFER_DENY", "Deferred-Denied"), + ("DENY", "Denied"), + ], + default="DENY", + max_length=20, + ), ), ] diff --git a/tjdests/apps/destinations/migrations/0004_rename_admitted_decision_admission_status.py b/tjdests/apps/destinations/migrations/0004_rename_admitted_decision_admission_status.py index 972b2e4..7a35647 100644 --- a/tjdests/apps/destinations/migrations/0004_rename_admitted_decision_admission_status.py +++ b/tjdests/apps/destinations/migrations/0004_rename_admitted_decision_admission_status.py @@ -6,13 +6,13 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('destinations', '0003_alter_decision_admitted'), + ("destinations", "0003_alter_decision_admitted"), ] operations = [ migrations.RenameField( - model_name='decision', - old_name='admitted', - new_name='admission_status', + model_name="decision", + old_name="admitted", + new_name="admission_status", ), ] diff --git a/tjdests/apps/destinations/migrations/0005_alter_decision_admission_status.py b/tjdests/apps/destinations/migrations/0005_alter_decision_admission_status.py index 9fcf797..453754d 100644 --- a/tjdests/apps/destinations/migrations/0005_alter_decision_admission_status.py +++ b/tjdests/apps/destinations/migrations/0005_alter_decision_admission_status.py @@ -6,13 +6,25 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('destinations', '0004_rename_admitted_decision_admission_status'), + ("destinations", "0004_rename_admitted_decision_admission_status"), ] operations = [ migrations.AlterField( - model_name='decision', - name='admission_status', - field=models.CharField(choices=[('ADMIT', 'Admitted'), ('WAITLIST', 'Waitlisted'), ('WAITLIST_ADMIT', 'Waitlist-Admitted'), ('WAITLIST_DENY', 'Waitlist-Denied'), ('DEFER', 'Deferred'), ('DEFER_ADMIT', 'Deferred-Admitted'), ('DEFER_DENY', 'Deferred-Denied'), ('DENY', 'Denied')], max_length=20), + model_name="decision", + name="admission_status", + field=models.CharField( + choices=[ + ("ADMIT", "Admitted"), + ("WAITLIST", "Waitlisted"), + ("WAITLIST_ADMIT", "Waitlist-Admitted"), + ("WAITLIST_DENY", "Waitlist-Denied"), + ("DEFER", "Deferred"), + ("DEFER_ADMIT", "Deferred-Admitted"), + ("DEFER_DENY", "Deferred-Denied"), + ("DENY", "Denied"), + ], + max_length=20, + ), ), ] diff --git a/tjdests/apps/destinations/migrations/0006_alter_college_ceeb_code.py b/tjdests/apps/destinations/migrations/0006_alter_college_ceeb_code.py index d74bcb2..7eb637f 100644 --- a/tjdests/apps/destinations/migrations/0006_alter_college_ceeb_code.py +++ b/tjdests/apps/destinations/migrations/0006_alter_college_ceeb_code.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('destinations', '0005_alter_decision_admission_status'), + ("destinations", "0005_alter_decision_admission_status"), ] operations = [ migrations.AlterField( - model_name='college', - name='ceeb_code', - field=models.CharField(max_length=10, verbose_name='CEEB Code'), + model_name="college", + name="ceeb_code", + field=models.CharField(max_length=10, verbose_name="CEEB Code"), ), ] diff --git a/tjdests/apps/destinations/models.py b/tjdests/apps/destinations/models.py index 976a162..6ca379f 100644 --- a/tjdests/apps/destinations/models.py +++ b/tjdests/apps/destinations/models.py @@ -4,7 +4,8 @@ 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) @@ -33,7 +34,9 @@ class Decision(models.Model): user = models.ForeignKey("authentication.User", on_delete=models.CASCADE) - decision_type = models.CharField(max_length=20, choices=DECISION_TYPE_CHOICES, null=True) + decision_type = models.CharField( + max_length=20, choices=DECISION_TYPE_CHOICES, null=True + ) ADMIT = "ADMIT" WAITLIST_ADMIT = "WAITLIST_ADMIT" @@ -55,7 +58,8 @@ 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): diff --git a/tjdests/apps/destinations/urls.py b/tjdests/apps/destinations/urls.py index 24f1a96..a8ecf31 100644 --- a/tjdests/apps/destinations/urls.py +++ b/tjdests/apps/destinations/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from . import views app_name = "destinations" @@ -6,4 +7,4 @@ app_name = "destinations" urlpatterns = [ path("", views.StudentDestinationListView.as_view(), name="students"), path("colleges", views.CollegeDestinationListView.as_view(), name="colleges"), -] \ No newline at end of file +] diff --git a/tjdests/apps/destinations/views.py b/tjdests/apps/destinations/views.py index aad9f2e..4cf6362 100644 --- a/tjdests/apps/destinations/views.py +++ b/tjdests/apps/destinations/views.py @@ -1,10 +1,10 @@ -from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db.models import Count, Q from django.shortcuts import get_object_or_404 from django.views.generic import ListView -from .models import College, Decision from ..authentication.models import User +from .models import College, Decision class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListView): @@ -12,7 +12,9 @@ class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListVi paginate_by = 20 def get_queryset(self): - queryset = User.objects.filter(publish_data=True, is_senior=True).order_by("last_name", "first_name") + queryset = User.objects.filter(publish_data=True, is_senior=True).order_by( + "last_name", "first_name" + ) college_id = self.request.GET.get("college", None) if college_id is not None: @@ -22,7 +24,8 @@ class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListVi return queryset def get_context_data(self, *, object_list=None, **kwargs): - context = super(StudentDestinationListView, self).get_context_data(**kwargs) + context = super(StudentDestinationListView, + self).get_context_data(**kwargs) college_id = self.request.GET.get("college", None) if college_id is not None: @@ -39,31 +42,71 @@ class StudentDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListVi class CollegeDestinationListView(LoginRequiredMixin, UserPassesTestMixin, ListView): model = College paginate_by = 20 - queryset = College.objects.annotate(count_decisions=Count("decision", filter=Q(decision__user__publish_data=True)), - count_admit=Count("decision", - filter=Q(decision__admission_status=Decision.ADMIT, - decision__user__publish_data=True)), - count_waitlist=Count("decision", - filter=Q(decision__admission_status=Decision.WAITLIST, - decision__user__publish_data=True)), - count_waitlist_admit=Count("decision", filter=Q( - decision__admission_status=Decision.WAITLIST_ADMIT, - decision__user__publish_data=True)), - count_waitlist_deny=Count("decision", filter=Q( - decision__admission_status=Decision.WAITLIST_DENY, - decision__user__publish_data=True)), - count_defer=Count("decision", - filter=Q(decision__admission_status=Decision.DEFER, - decision__user__publish_data=True)), - count_defer_admit=Count("decision", filter=Q( - decision__admission_status=Decision.DEFER_ADMIT, - decision__user__publish_data=True)), - count_defer_deny=Count("decision", - filter=Q(decision__admission_status=Decision.DEFER_DENY, - decision__user__publish_data=True)), - count_deny=Count("decision", filter=Q(decision__admission_status=Decision.DENY, - decision__user__publish_data=True)), - ).filter(count_decisions__gte=1).order_by("name") + queryset = ( + College.objects.annotate( + count_decisions=Count( + "decision", filter=Q(decision__user__publish_data=True) + ), + count_admit=Count( + "decision", + filter=Q( + decision__admission_status=Decision.ADMIT, + decision__user__publish_data=True, + ), + ), + count_waitlist=Count( + "decision", + filter=Q( + decision__admission_status=Decision.WAITLIST, + decision__user__publish_data=True, + ), + ), + count_waitlist_admit=Count( + "decision", + filter=Q( + decision__admission_status=Decision.WAITLIST_ADMIT, + decision__user__publish_data=True, + ), + ), + count_waitlist_deny=Count( + "decision", + filter=Q( + decision__admission_status=Decision.WAITLIST_DENY, + decision__user__publish_data=True, + ), + ), + count_defer=Count( + "decision", + filter=Q( + decision__admission_status=Decision.DEFER, + decision__user__publish_data=True, + ), + ), + count_defer_admit=Count( + "decision", + filter=Q( + decision__admission_status=Decision.DEFER_ADMIT, + decision__user__publish_data=True, + ), + ), + count_defer_deny=Count( + "decision", + filter=Q( + decision__admission_status=Decision.DEFER_DENY, + decision__user__publish_data=True, + ), + ), + count_deny=Count( + "decision", + filter=Q( + decision__admission_status=Decision.DENY, + decision__user__publish_data=True, + ), + ), + ) + .filter(count_decisions__gte=1) + .order_by("name") + ) def test_func(self): return self.request.user.accepted_terms diff --git a/tjdests/apps/profile/forms.py b/tjdests/apps/profile/forms.py index fc1220c..774990b 100644 --- a/tjdests/apps/profile/forms.py +++ b/tjdests/apps/profile/forms.py @@ -1,10 +1,8 @@ +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit from django import forms from tjdests.apps.authentication.models import User - -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Submit - from tjdests.apps.destinations.models import Decision @@ -12,16 +10,24 @@ class ProfilePublishForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' + self.helper.form_method = "post" - self.helper.add_input(Submit('submit', 'Submit')) + self.helper.add_input(Submit("submit", "Submit")) - self.fields["attending_decision"].queryset = Decision.objects.filter(user=self.instance, admission_status__in=[Decision.ADMIT, Decision.WAITLIST_ADMIT, Decision.DEFER_ADMIT]) + self.fields["attending_decision"].queryset = Decision.objects.filter( + user=self.instance, + admission_status__in=[ + Decision.ADMIT, + Decision.WAITLIST_ADMIT, + Decision.DEFER_ADMIT, + ], + ) class Meta: model = User fields = ["publish_data", "biography", "attending_decision"] + class DecisionForm(forms.ModelForm): class Meta: model = Decision diff --git a/tjdests/apps/profile/urls.py b/tjdests/apps/profile/urls.py index b10fc10..dad6e29 100644 --- a/tjdests/apps/profile/urls.py +++ b/tjdests/apps/profile/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from . import views app_name = "profile" @@ -6,9 +7,25 @@ app_name = "profile" urlpatterns = [ path("", views.profile_view, name="index"), path("testscore/add", views.TestScoreCreateView.as_view(), name="testscores_add"), - path("testscore/edit/", views.TestScoreUpdateView.as_view(), name="testscores_edit"), - path("testscore/delete/", views.TestScoreDeleteView.as_view(), name="testscores_delete"), + path( + "testscore/edit/", + views.TestScoreUpdateView.as_view(), + name="testscores_edit", + ), + path( + "testscore/delete/", + views.TestScoreDeleteView.as_view(), + name="testscores_delete", + ), path("decision/add", views.DecisionCreateView.as_view(), name="decision_add"), - path("decision/edit/", views.DecisionUpdateView.as_view(), name="decision_edit"), - path("decision/delete/", views.DecisionDeleteView.as_view(), name="decision_delete"), -] \ No newline at end of file + path( + "decision/edit/", + views.DecisionUpdateView.as_view(), + name="decision_edit", + ), + path( + "decision/delete/", + views.DecisionDeleteView.as_view(), + name="decision_delete", + ), +] diff --git a/tjdests/apps/profile/views.py b/tjdests/apps/profile/views.py index d2a240b..852d006 100644 --- a/tjdests/apps/profile/views.py +++ b/tjdests/apps/profile/views.py @@ -1,16 +1,16 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.messages.views import SuccessMessageMixin from django.http import HttpRequest from django.shortcuts import render from django.urls import reverse -from django.views.generic import CreateView, UpdateView, DeleteView +from django.views.generic import CreateView, DeleteView, UpdateView from tjdests.apps.authentication.decorators import require_accept_tos -from tjdests.apps.destinations.models import TestScore, Decision +from tjdests.apps.destinations.models import Decision, TestScore -from .forms import ProfilePublishForm, DecisionForm +from .forms import DecisionForm, ProfilePublishForm @login_required @@ -29,12 +29,18 @@ def profile_view(request: HttpRequest): else: profile_form = ProfilePublishForm(instance=request.user) - context = {"test_scores_list": test_scores, "decisions_list": decisions, "profile_form": profile_form} + context = { + "test_scores_list": test_scores, + "decisions_list": decisions, + "profile_form": profile_form, + } return render(request, "profile/profile.html", context=context) -class TestScoreCreateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, CreateView): +class TestScoreCreateView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, CreateView +): model = TestScore fields = ["exam_type", "exam_score"] template_name = "profile/testscore_form.html" @@ -51,7 +57,9 @@ class TestScoreCreateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTes return reverse("profile:index") -class TestScoreUpdateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, UpdateView): +class TestScoreUpdateView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, UpdateView +): model = TestScore fields = ["exam_type", "exam_score"] template_name = "profile/testscore_form.html" @@ -72,7 +80,9 @@ class TestScoreUpdateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTes return reverse("profile:index") -class TestScoreDeleteView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, DeleteView): +class TestScoreDeleteView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, DeleteView +): model = TestScore template_name = "profile/testscore_delete.html" success_message = "Test score deleted successfully." @@ -87,7 +97,10 @@ class TestScoreDeleteView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTes def get_success_url(self): return reverse("profile:index") -class DecisionCreateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, CreateView): + +class DecisionCreateView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, CreateView +): model = Decision fields = ["college", "decision_type", "admission_status"] template_name = "profile/decision_form.html" @@ -104,7 +117,9 @@ class DecisionCreateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTest return reverse("profile:index") -class DecisionUpdateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, UpdateView): +class DecisionUpdateView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, UpdateView +): model = Decision fields = ["college", "decision_type", "admission_status"] template_name = "profile/decision_form.html" @@ -125,7 +140,9 @@ class DecisionUpdateView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTest return reverse("profile:index") -class DecisionDeleteView(LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, DeleteView): +class DecisionDeleteView( + LoginRequiredMixin, SuccessMessageMixin, UserPassesTestMixin, DeleteView +): model = Decision template_name = "profile/decision_delete.html" success_message = "Decision deleted successfully." diff --git a/tjdests/asgi.py b/tjdests/asgi.py index 5075bff..759c262 100644 --- a/tjdests/asgi.py +++ b/tjdests/asgi.py @@ -11,6 +11,6 @@ import os from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tjdests.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tjdests.settings") application = get_asgi_application() diff --git a/tjdests/settings/__init__.py b/tjdests/settings/__init__.py index 7687d07..4373172 100644 --- a/tjdests/settings/__init__.py +++ b/tjdests/settings/__init__.py @@ -21,7 +21,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-7nju0o%j&gz7&v^05iuq*tn$_iwvtjh1cq26@is(u2d4snkum5' +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 @@ -32,62 +32,61 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", "crispy_forms", "crispy_bootstrap5", "social_django", "django_extensions", "bootstrap_pagination", - 'tjdests.apps.authentication', - 'tjdests.apps.destinations', + "tjdests.apps.authentication", + "tjdests.apps.destinations", "tjdests.apps.profile", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'tjdests.urls' +ROOT_URLCONF = "tjdests.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'] - , - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'tjdests.apps.context_processors.settings_renderer', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "tjdests.apps.context_processors.settings_renderer", ], }, }, ] -WSGI_APPLICATION = 'tjdests.wsgi.application' +WSGI_APPLICATION = "tjdests.wsgi.application" # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -97,22 +96,25 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] AUTH_USER_MODEL = "authentication.User" -AUTHENTICATION_BACKENDS = ("tjdests.apps.authentication.oauth.IonOauth2", "django.contrib.auth.backends.ModelBackend",) +AUTHENTICATION_BACKENDS = ( + "tjdests.apps.authentication.oauth.IonOauth2", + "django.contrib.auth.backends.ModelBackend", +) SOCIAL_AUTH_REDIRECT_IS_HTTPS = False SOCIAL_AUTH_ION_KEY = "" @@ -122,9 +124,9 @@ SOCIAL_AUTH_ION_SECRET = "" # Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -136,14 +138,14 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" STATIC_ROOT = os.path.join(BASE_DIR, "serve/") STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/"),) # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" LOGIN_REDIRECT_URL = "authentication:tos" LOGOUT_REDIRECT_URL = "authentication:index" diff --git a/tjdests/settings/secret.sample.py b/tjdests/settings/secret.sample.py index 6a4d781..0092c11 100644 --- a/tjdests/settings/secret.sample.py +++ b/tjdests/settings/secret.sample.py @@ -10,7 +10,7 @@ DEBUG = True ALLOWED_HOSTS = [] # secret -SECRET_KEY = 'supersecret' +SECRET_KEY = "supersecret" # OAuth SOCIAL_AUTH_ION_KEY = "ionkey" diff --git a/tjdests/urls.py b/tjdests/urls.py index ce1c41e..d3d5b9c 100644 --- a/tjdests/urls.py +++ b/tjdests/urls.py @@ -14,12 +14,15 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path, include +from django.urls import include, path urlpatterns = [ - path('djangoadmin/', admin.site.urls), + path("djangoadmin/", admin.site.urls), path("", include("tjdests.apps.authentication.urls", namespace="authentication")), path("oauth/", include("social_django.urls", namespace="social")), - path("destinations/", include("tjdests.apps.destinations.urls", namespace="destinations")), + path( + "destinations/", + include("tjdests.apps.destinations.urls", namespace="destinations"), + ), path("profile/", include("tjdests.apps.profile.urls", namespace="profile")), ] diff --git a/tjdests/wsgi.py b/tjdests/wsgi.py index 2b83ce4..e88d492 100644 --- a/tjdests/wsgi.py +++ b/tjdests/wsgi.py @@ -11,6 +11,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tjdests.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tjdests.settings") application = get_wsgi_application()