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

#998 admin api categories #1017

Merged
merged 17 commits into from
Dec 16, 2024
10 changes: 9 additions & 1 deletion BackEnd/administration/factories.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import factory.django

from authentication.models import CustomUser
from profiles.models import Profile
from profiles.models import Profile, Category


class AdminUserFactory(factory.django.DjangoModelFactory):
Expand Down Expand Up @@ -39,3 +39,11 @@ class Meta:
official_name = "Test official name"
startup_idea = "Test startup idea"
is_deleted = False


class AdminCategoryFactory(factory.django.DjangoModelFactory):
class Meta:
model = Category
django_get_or_create = ("name",)

name = factory.Sequence(lambda n: f"category {n + 1}")
23 changes: 17 additions & 6 deletions BackEnd/administration/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ class UsersFilter(FilterSet):
/?ordering=id asc or /?ordering=-id desc
"""

id = filters.CharFilter(lookup_expr="icontains")
id = filters.NumberFilter(lookup_expr="contains")
surname = filters.CharFilter(lookup_expr="icontains")
email = filters.CharFilter(lookup_expr="icontains")
is_active = filters.CharFilter(lookup_expr="icontains")
is_staff = filters.CharFilter(lookup_expr="icontains")
is_superuser = filters.CharFilter(lookup_expr="icontains")
is_deleted = filters.BooleanFilter(method="filter_is_deleted")
is_active = filters.BooleanFilter()
is_staff = filters.BooleanFilter()
is_superuser = filters.BooleanFilter()
is_deleted = filters.BooleanFilter(method="is_deleted_filter")
company_name = filters.CharFilter(
field_name="profile__name", lookup_expr="icontains"
)
registration_date = filters.CharFilter(
field_name="profile__created_at", lookup_expr="icontains"
)

def filter_is_deleted(self, queryset, name, value):
def is_deleted_filter(self, queryset, name, value):
if value:
queryset = queryset.filter(email__startswith="is_deleted_")
return queryset
Expand All @@ -43,3 +43,14 @@ def filter_is_deleted(self, queryset, name, value):
("profile__created_at", "registration_date"),
)
)


class CategoriesFilter(FilterSet):
id = filters.NumberFilter(lookup_expr="contains")
name = filters.CharFilter(lookup_expr="icontains")
ordering = filters.OrderingFilter(
fields=(
("id", "id"),
("name", "name"),
)
)
23 changes: 23 additions & 0 deletions BackEnd/administration/serializers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from utils.administration.feedback_category import FeedbackCategory
from authentication.models import CustomUser
from profiles.models import (
Profile,
Region,
Category,
)
from utils.administration.create_password import generate_password
from utils.administration.send_email import send_email_about_admin_registration
Expand Down Expand Up @@ -215,6 +217,27 @@ class FeedbackSerializer(serializers.Serializer):
)


class CategoriesListSerializer(serializers.ModelSerializer):
name = serializers.CharField(
validators=[
UniqueValidator(
queryset=Category.objects.all(),
message="Category with this name already exists.",
)
]
)

class Meta:
model = Category
fields = ("id", "name")


class CategoryDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ("name",)


class StatisticsSerializer(serializers.Serializer):
companies_count = serializers.IntegerField()
investors_count = serializers.IntegerField()
Expand Down
100 changes: 100 additions & 0 deletions BackEnd/administration/tests/test_admin_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from rest_framework import status
from rest_framework.test import APITestCase

from administration.factories import (
AdminCategoryFactory,
AdminUserFactory,
)

from utils.dump_response import dump # noqa
from utils.unittest_helper import AnyInt, AnyStr


class TestAdminCategoryAPIUserNotStaff(APITestCase):
def setUp(self):
self.user = AdminUserFactory(
is_staff=False,
is_active=True,
)
self.category = AdminCategoryFactory()

def test_get_category_users_not_staff(self):
self.client.force_authenticate(self.user)
response = self.client.get(path="/api/admin/categories/")
self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)

def test_get_category_id_users_not_staff(self):
self.client.force_authenticate(self.user)
response = self.client.get(path="/api/admin/categories/1/")
self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)


class TestAdminCategoryAPIUserStaff(APITestCase):
def setUp(self):
self.user = AdminUserFactory(
is_staff=True,
is_active=True,
)
self.categories = AdminCategoryFactory.create_batch(2)

def test_get_categories_users_staff(self):
self.client.force_authenticate(self.user)
response = self.client.get(path="/api/admin/categories/")
data = [
{
"id": AnyInt(),
"name": AnyStr(),
},
{
"id": AnyInt(),
"name": AnyStr(),
},
]
self.assertEqual(data, response.json()["results"])
self.assertEqual(status.HTTP_200_OK, response.status_code)

def test_get_categories_id_users_staff(self):
self.client.force_authenticate(self.user)
response = self.client.get(path="/api/admin/categories/3/")
data = {"name": AnyStr()}
self.assertEqual(status.HTTP_200_OK, response.status_code)
self.assertEqual(data, response.json())

def test_post_new_categories_users_staff(self):
self.client.force_authenticate(self.user)
data = {"name": "some category"}
response = self.client.post(path="/api/admin/categories/", data=data)
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
self.assertEqual(response.json()["name"], data["name"])
self.assertIn("id", response.json())

def test_post_unique_categories_users_staff(self):
self.existing_name = AdminCategoryFactory.create(
name="existing category"
)
self.client.force_authenticate(self.user)
data = {"name": "existing category"}
message = "Category with this name already exists."
response = self.client.post(path="/api/admin/categories/", data=data)
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code)
self.assertEqual(response.json()["name"][0], message)

def test_put_categories_id_users_staff(self):
self.category = AdminCategoryFactory.create()
self.client.force_authenticate(self.user)
data = {"name": "category 1212"}
response = self.client.put(
path=f"/api/admin/categories/{self.category.id}/", data=data
)
self.assertEqual(status.HTTP_200_OK, response.status_code)
self.assertEqual(response.json()["name"], data["name"])

def test_patch_categories_id_users_staff(self):
self.category = AdminCategoryFactory.create()
self.client.force_authenticate(self.user)
data = {"name": "category 77"}
response = self.client.patch(
path=f"/api/admin/categories/{self.category.id}/", data=data
)
self.assertEqual(status.HTTP_200_OK, response.status_code)
self.assertEqual(response.json()["name"], data["name"])
15 changes: 8 additions & 7 deletions BackEnd/administration/tests/test_admin_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
AdminProfileFactory,
)

from utils.unittest_helper import AnyInt, AnyStr
from utils.dump_response import dump # noqa


Expand Down Expand Up @@ -52,14 +53,14 @@ def test_get_profiles_structure_json(self):
response = self.client.get(path="/api/admin/profiles/")
data = [
{
"id": 4,
"id": AnyInt(),
"name": "Test person",
"is_registered": True,
"is_startup": True,
"person": {
"name": "Test person 7",
"surname": "Test person 7 surname",
"email": "[email protected]",
"name": AnyStr(),
"surname": AnyStr(),
"email": AnyStr(),
"is_active": True,
"is_staff": True,
"is_superuser": False,
Expand Down Expand Up @@ -89,9 +90,9 @@ def test_get_profile_id_authenticated(self):
"categories": [],
"activities": [],
"person": {
"name": "Test person 1",
"surname": "Test person 1 surname",
"email": "[email protected]",
"name": AnyStr(),
"surname": AnyStr(),
"email": AnyStr(),
"is_active": True,
"is_staff": True,
"is_superuser": False,
Expand Down
12 changes: 12 additions & 0 deletions BackEnd/administration/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
ModerationEmailView,
FeedbackView,
CreateAdminUserView,
CategoriesListView,
CategoryDetailView,
SendMessageView,
)

Expand All @@ -35,6 +37,16 @@
path("contacts/", ContactsView.as_view(), name="contacts"),
path("feedback/", FeedbackView.as_view(), name="feedback"),
path("admin_create/", CreateAdminUserView.as_view(), name="admin-create"),
path(
"categories/",
CategoriesListView.as_view(),
name="categories",
),
path(
"categories/<pk>/",
CategoryDetailView.as_view(),
name="category_detail",
),
path(
"users/<pk>/send_message/",
SendMessageView.as_view(),
Expand Down
40 changes: 39 additions & 1 deletion BackEnd/administration/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
)
from rest_framework.generics import (
ListAPIView,
ListCreateAPIView,
RetrieveAPIView,
RetrieveUpdateDestroyAPIView,
RetrieveUpdateAPIView,
Expand All @@ -25,15 +26,20 @@
AdminUserDetailSerializer,
AutoModerationHoursSerializer,
ModerationEmailSerializer,
CategoriesListSerializer,
CategoryDetailSerializer,
StatisticsSerializer,
)
from administration.pagination import ListPagination
from administration.models import AutoModeration, ModerationEmail
from authentication.models import CustomUser
from profiles.models import Profile
from profiles.models import Profile, Category
from .permissions import IsStaffUser, IsStaffUserOrReadOnly, IsSuperUser
from .serializers import FeedbackSerializer
from utils.administration.send_email_feedback import send_email_feedback

from django_filters.rest_framework import DjangoFilterBackend
from .filters import UsersFilter, CategoriesFilter
from utils.administration.send_email_notification import send_email_to_user
from .filters import UsersFilter

Expand Down Expand Up @@ -221,6 +227,38 @@ def perform_create(self, serializer):
send_email_feedback(email, message, category)


class CategoriesListView(ListCreateAPIView):
"""
Manage categories
### Query Parameters:
- **id** / **name**

### Ordering:
- Use the `ordering` parameter to sort the results.
- Example: `/categories/?ordering=id` (ascending by ID) or `/categories/?ordering=-id` (descending by ID).

### Filters:
- Filters are applied using `DjangoFilterBackend`. All the above query parameters are supported for filtering.
"""

permission_classes = [IsStaffUser]
serializer_class = CategoriesListSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = CategoriesFilter
pagination_class = ListPagination
queryset = Category.objects.all().order_by("id")


class CategoryDetailView(RetrieveUpdateAPIView):
"""
Modify activity category
"""

permission_classes = [IsStaffUser]
serializer_class = CategoryDetailSerializer
queryset = Category.objects.all()


class SendMessageView(CreateAPIView):
"""
API endpoint for sending a custom email message to a specific user.
Expand Down
Loading