Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API endpoints and dynamic responses #9

Merged
merged 5 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions backend/dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ click==8.1.7
# via
# black
# pip-tools
coverage==7.5.3
coverage==7.5.4
# via -r dev-requirements.in
curtsies==0.4.2
# via bpython
Expand All @@ -42,9 +42,9 @@ django-debug-toolbar==4.4.2
# via -r dev-requirements.in
factory-boy==3.3.0
# via -r dev-requirements.in
faker==25.8.0
faker==26.0.0
# via factory-boy
flake8==7.0.0
flake8==7.1.0
# via -r dev-requirements.in
greenlet==3.0.3
# via bpython
Expand All @@ -67,7 +67,7 @@ pip-tools==7.4.1
# via -r dev-requirements.in
platformdirs==4.2.2
# via black
pycodestyle==2.11.1
pycodestyle==2.12.0
# via flake8
pyflakes==3.2.0
# via flake8
Expand All @@ -94,7 +94,7 @@ sqlparse==0.5.0
# django-debug-toolbar
tblib==3.0.0
# via -r dev-requirements.in
urllib3==2.2.1
urllib3==2.2.2
# via
# -c requirements.txt
# requests
Expand Down
6 changes: 4 additions & 2 deletions backend/project/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ class UserAdmin(BaseUserAdmin):
"first_name",
"last_name",
"is_active",
"is_staff",
"is_superuser",
"date_joined",
"last_login",
)
list_filter = (
"is_superuser",
"is_active",
)
search_fields = ("email", "first_name", "last_name")
ordering = ("email",)
fieldsets = (
Expand All @@ -37,7 +40,6 @@ class UserAdmin(BaseUserAdmin):
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
Expand Down
1 change: 0 additions & 1 deletion backend/project/accounts/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ def create_user(self, email, password=None, **extra_fields):
return user

def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)

return self.create_user(email, password, **extra_fields)
10 changes: 1 addition & 9 deletions backend/project/accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-06-12 20:15
# Generated by Django 5.0.6 on 2024-06-28 13:23

import django.db.models.functions.datetime
import django.utils.timezone
Expand Down Expand Up @@ -72,14 +72,6 @@ class Migration(migrations.Migration):
verbose_name="date joined",
),
),
(
"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(
Expand Down
9 changes: 4 additions & 5 deletions backend/project/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ class User(AbstractBaseUser, PermissionsMixin):
date_joined = models.DateTimeField(
_("date joined"), default=timezone.now, db_default=Now()
)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
is_active = models.BooleanField(
_("active"),
default=True,
Expand All @@ -37,6 +32,10 @@ class User(AbstractBaseUser, PermissionsMixin):
EMAIL_FIELD = "email"
USERNAME_FIELD = "email"

@property
def is_staff(self):
return self.is_superuser

class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
Expand Down
17 changes: 17 additions & 0 deletions backend/project/accounts/tests/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import factory
from factory import faker

from ..models import User


class UserFactory(factory.django.DjangoModelFactory):
email = faker.Faker("email")
password = factory.PostGenerationMethodCall("set_password", "password")
is_active = True

class Meta:
model = User


class SuperUserFactory(UserFactory):
is_superuser = True
16 changes: 16 additions & 0 deletions backend/project/accounts/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.test import TestCase

from .factories import SuperUserFactory, UserFactory


class AccountAdminTestCase(TestCase):
def test_superuser_ca(self):
"""Test that only superuser can log in to the admin site."""
simple_user = UserFactory()
self.client.force_login(simple_user)
response = self.client.get("/admin/")
self.assertEqual(response.status_code, 302)
super_user = SuperUserFactory()
self.client.force_login(super_user)
response = self.client.get("/admin/")
self.assertEqual(response.status_code, 200)
14 changes: 14 additions & 0 deletions backend/project/accounts/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.test import TestCase

from .factories import SuperUserFactory, UserFactory


class AccountTestCase(TestCase):
def test_is_staff(self):
"""Test that is_staff is same value as is_superuser"""
user = UserFactory()
self.assertFalse(user.is_staff)
self.assertFalse(user.is_superuser)
super_user = SuperUserFactory()
self.assertTrue(super_user.is_staff)
self.assertTrue(super_user.is_superuser)
71 changes: 0 additions & 71 deletions backend/project/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,71 +0,0 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.viewsets import GenericViewSet

from project.accounts.models import User
from project.api.filters import EventFilterSet
from project.api.serializers import (
AccountSerializer,
EventDetailSerializer,
EventListSerializer,
EventTypeSerializer,
SettingsSerializer,
)
from project.events.models import Event, EventType


class SettingsApiView(GenericAPIView):
serializer_class = SettingsSerializer

def get(self, request):
data = {}
event_types = EventType.objects.prefetch_related("sub_types").all()
event_types_serialized = EventTypeSerializer(
event_types, many=True, context={"request": request}
)
data["event_types"] = event_types_serialized.data
data["event_url"] = reverse("api:events-list", request=request)
return Response(data)


class EventViewSet(viewsets.ModelViewSet):
filter_backends = (DjangoFilterBackend,)
filterset_class = EventFilterSet

def get_serializer_class(self):
if self.action == "list":
return EventListSerializer
return EventDetailSerializer

def get_queryset(self):
qs = Event.objects.all().select_related("source", "event_subtype__event_type")
if self.action not in ["list", "retrieve"] or self.request.user.is_anonymous:
# list and retrieve are public
if self.request.user.is_anonymous:
# anonymous can't change anything
qs = qs.none()
elif not self.request.user.is_staff and not self.request.user.is_superuser:
# nor anonymous, nor admin
qs = self.request.user.events.all()
return qs

def perform_create(self, serializer):
observer = self.request.user if self.request.user.is_authenticated else None
serializer.save(observer=observer)


class AccountViewSet(CreateModelMixin, GenericViewSet):
queryset = User.objects.all()
serializer_class = AccountSerializer

@action(detail=False, methods=["get"])
def mine(self, request, *args, **kwargs):
if self.request.user.is_anonymous:
return Response({}, status=status.HTTP_404_NOT_FOUND)
serializer = self.get_serializer(request.user)
return Response(serializer.data)
8 changes: 8 additions & 0 deletions backend/project/api/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class ApiConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "project.api"
verbose_name = _("API")
16 changes: 11 additions & 5 deletions backend/project/api/filters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
from django_filters.fields import DateRangeField
from django_filters.rest_framework import FilterSet
from django_filters.rest_framework import FilterSet, filters

from project.events.models import Event
from project.observations.models import Observation


class EventFilterSet(FilterSet):
class ObservationFilterSet(FilterSet):
event_date = DateRangeField()
fields = filters.CharFilter(
method="filter_fields", help_text="filter fields you want to get"
)

def filter_fields(self, qs):
return qs

Check warning on line 14 in backend/project/api/filters.py

View check run for this annotation

Codecov / codecov/patch

backend/project/api/filters.py#L14

Added line #L14 was not covered by tests

class Meta:
model = Event
fields = ["event_date", "event_subtype"]
model = Observation
fields = ["event_date", "observation_subtype", "fields"]
120 changes: 0 additions & 120 deletions backend/project/api/serializers.py

This file was deleted.

Loading