Skip to content

Commit

Permalink
Linting commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wildjames committed Nov 23, 2024
1 parent 0c46fe3 commit 4aef7d5
Show file tree
Hide file tree
Showing 58 changed files with 642 additions and 345 deletions.
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: install dev build clean
.PHONY: install dev build clean lint

install:
# Install backend dependencies
Expand All @@ -25,3 +25,9 @@ clean:
rm -rf todoqueue_backend/staticfiles
find . -name '*.pyc' -delete
find . -name '__pycache__' -delete

lint:
# Lint Python code with Black
black todoqueue_backend
# Lint JavaScript code with ESLint
cd todoqueue_frontend && npx eslint .
4 changes: 2 additions & 2 deletions todoqueue_backend/accounts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@


class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
default_auto_field = "django.db.models.BigAutoField"
name = "accounts"
75 changes: 59 additions & 16 deletions todoqueue_backend/accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,72 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
("auth", "0012_alter_user_first_name_max_length"),
]

operations = [
migrations.CreateModel(
name='CustomUser',
name="CustomUser",
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')),
('email', models.EmailField(max_length=254, unique=True)),
('username', models.CharField(max_length=30, unique=True)),
('date_joined', models.DateTimeField(default=django.utils.timezone.now)),
('is_active', models.BooleanField(default=True)),
('is_staff', models.BooleanField(default=False)),
('brownie_point_credit', models.JSONField(default=dict)),
('brownie_point_debit', models.JSONField(default=dict)),
('groups', 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')),
('user_permissions', 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')),
(
"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",
),
),
("email", models.EmailField(max_length=254, unique=True)),
("username", models.CharField(max_length=30, unique=True)),
(
"date_joined",
models.DateTimeField(default=django.utils.timezone.now),
),
("is_active", models.BooleanField(default=True)),
("is_staff", models.BooleanField(default=False)),
("brownie_point_credit", models.JSONField(default=dict)),
("brownie_point_debit", models.JSONField(default=dict)),
(
"groups",
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",
),
),
(
"user_permissions",
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",
),
),
],
options={
'abstract': False,
"abstract": False,
},
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
class Migration(migrations.Migration):

dependencies = [
('accounts', '0001_initial'),
("accounts", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name='customuser',
name='username',
field=models.CharField(max_length=30, unique=True, validators=[accounts.models.validate_profanity]),
model_name="customuser",
name="username",
field=models.CharField(
max_length=30,
unique=True,
validators=[accounts.models.validate_profanity],
),
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
class Migration(migrations.Migration):

dependencies = [
('accounts', '0002_alter_customuser_username'),
("accounts", "0002_alter_customuser_username"),
]

operations = [
migrations.AlterField(
model_name='customuser',
name='username',
field=models.CharField(max_length=30, validators=[accounts.models.validate_profanity]),
model_name="customuser",
name="username",
field=models.CharField(
max_length=30, validators=[accounts.models.validate_profanity]
),
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
class Migration(migrations.Migration):

dependencies = [
('accounts', '0003_alter_customuser_username'),
("accounts", "0003_alter_customuser_username"),
]

operations = [
migrations.AddField(
model_name='customuser',
name='has_logged_in',
model_name="customuser",
name="has_logged_in",
field=models.BooleanField(default=False),
),
]
2 changes: 1 addition & 1 deletion todoqueue_backend/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)

has_logged_in = models.BooleanField(default=False)

brownie_point_credit = models.JSONField(default=dict)
Expand Down
10 changes: 8 additions & 2 deletions todoqueue_backend/accounts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ class Meta(CustomUserSerializer.Meta):

class CustomUserRegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
validators=[UniqueValidator(queryset=get_user_model().objects.all()), validate_profanity]
validators=[
UniqueValidator(queryset=get_user_model().objects.all()),
validate_profanity,
]
)
username = serializers.CharField(
validators=[UniqueValidator(queryset=get_user_model().objects.all()), validate_profanity]
validators=[
UniqueValidator(queryset=get_user_model().objects.all()),
validate_profanity,
]
)
password = serializers.CharField(write_only=True)

Expand Down
45 changes: 24 additions & 21 deletions todoqueue_backend/accounts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from rest_framework_simplejwt.tokens import RefreshToken

from logging import getLogger

logger = getLogger(__name__)

from .serializers import CustomUserSerializer
Expand Down Expand Up @@ -173,44 +174,46 @@ def test_complete_forgot_password(self):
)

def test_register_existing_email(self):
url = reverse('register')
url = reverse("register")
data = {
"email": "[email protected]",
"username": "someotheruser",
"password": "somepass"
"password": "somepass",
}
response = self.client.post(url, data, format='json')
response = self.client.post(url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('detail', response.data)
self.assertEqual(response.data['detail'], 'User with this email already exists.')
self.assertIn("detail", response.data)
self.assertEqual(
response.data["detail"], "User with this email already exists."
)

def test_failed_login(self):
url = reverse('token_obtain_pair')
data = {
"email": "[email protected]",
"password": "wrongpassword"
}
response = self.client.post(url, data, format='json')
url = reverse("token_obtain_pair")
data = {"email": "[email protected]", "password": "wrongpassword"}
response = self.client.post(url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
self.assertEqual(response.data["detail"], "No active account found with the given credentials")
self.assertEqual(
response.data["detail"],
"No active account found with the given credentials",
)

def test_custom_user_viewset_list(self):
# First get a token for the user
token = RefreshToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.access_token}')
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token.access_token}")

# Now, retrieve list of users
url = reverse('customuser-list')
response = self.client.get(url, format='json')
url = reverse("customuser-list")
response = self.client.get(url, format="json")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_custom_user_viewset_retrieve(self):
# First get a token for the user
token = RefreshToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.access_token}')
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token.access_token}")

# Now, retrieve the user's own details
url = reverse('customuser-detail', args=[self.user.id])
response = self.client.get(url, format='json')
url = reverse("customuser-detail", args=[self.user.id])
response = self.client.get(url, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['email'], self.user.email)
self.assertEqual(response.data["email"], self.user.email)
10 changes: 7 additions & 3 deletions todoqueue_backend/accounts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
from datetime import timedelta

from logging import getLogger, DEBUG, basicConfig

basicConfig(level=DEBUG)
logger = getLogger(__name__)

def is_rate_limited(identifier, request_type, max_attempts=5, period=timedelta(hours=1)):

def is_rate_limited(
identifier, request_type, max_attempts=5, period=timedelta(hours=1)
):
"""
Check if the identifier has exceeded the maximum number of attempts within the period.
"""
logger.debug("Checking rate limit")

current_time = timezone.now()
cache_key = f"password_reset_attempts_{request_type}_{identifier}"
attempts = cache.get(cache_key, [])
Expand All @@ -27,4 +31,4 @@ def is_rate_limited(identifier, request_type, max_attempts=5, period=timedelta(h
attempts.append(current_time)
cache.set(cache_key, attempts, period.total_seconds())

return False
return False
25 changes: 19 additions & 6 deletions todoqueue_backend/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,18 @@ def check_permissions(self, request):

class GetUserData(APIView):
permission_classes = (IsAuthenticated,)

def get(self, request):
"""Return the serialization of the user who authenticated this request"""
logger.info("Getting serialization of a single user")
user = request.user
serializer = CustomUserSerializer(user)
serialized_data = serializer.data
logger.info(f"User data: {serialized_data}")

return Response(serialized_data)


class AuthView(APIView):
permission_classes = (IsAuthenticated,)

Expand Down Expand Up @@ -94,7 +95,10 @@ class RegisterView(APIView):
def post(self, request):
# Check if the rate limit has been exceeded
if is_rate_limited(
request.META['REMOTE_ADDR'], "init_registration", max_attempts=20, period=timedelta(hours=1)
request.META["REMOTE_ADDR"],
"init_registration",
max_attempts=20,
period=timedelta(hours=1),
):
return Response(
{"detail": "Registration requests are limited to 20 per hour."},
Expand Down Expand Up @@ -184,7 +188,10 @@ class ConfirmRegistrationView(APIView):
def get(self, request, uidb64, token):
# Check if the rate limit has been exceeded
if is_rate_limited(
request.META['REMOTE_ADDR'], "confirm_registration", max_attempts=50, period=timedelta(hours=1)
request.META["REMOTE_ADDR"],
"confirm_registration",
max_attempts=50,
period=timedelta(hours=1),
):
return Response(
{"detail": "Please stop spamming the confirmation endpoint."},
Expand Down Expand Up @@ -216,7 +223,10 @@ class ForgotPasswordView(APIView):
def post(self, request):
# Check if the rate limit has been exceeded
if is_rate_limited(
request.META['REMOTE_ADDR'], "forgot_password", max_attempts=5, period=timedelta(hours=1)
request.META["REMOTE_ADDR"],
"forgot_password",
max_attempts=5,
period=timedelta(hours=1),
):
return Response(
{"detail": "Password reset requests are limited to 5 per hour."},
Expand Down Expand Up @@ -291,7 +301,10 @@ class CompleteForgotPasswordView(APIView):
def post(self, request, uidb64, token):
# Check if the rate limit has been exceeded
if is_rate_limited(
request.META['REMOTE_ADDR'], "new_password", max_attempts=20, period=timedelta(hours=1)
request.META["REMOTE_ADDR"],
"new_password",
max_attempts=20,
period=timedelta(hours=1),
):
return Response(
{"detail": "Please stop spamming the password reset endpoint."},
Expand Down
4 changes: 2 additions & 2 deletions todoqueue_backend/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todoqueue_backend.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "todoqueue_backend.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
Expand All @@ -18,5 +18,5 @@ def main():
execute_from_command_line(sys.argv)


if __name__ == '__main__':
if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions todoqueue_backend/tasks/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@


class TasksConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tasks'
default_auto_field = "django.db.models.BigAutoField"
name = "tasks"
Loading

0 comments on commit 4aef7d5

Please sign in to comment.