Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:ita-social-projects/Forum into #…
Browse files Browse the repository at this point in the history
…1056-IncorrectHeaderElementsOnAdminPage
  • Loading branch information
ohorodnykostap committed Jan 10, 2025
2 parents a02a290 + 1cd2da0 commit d8f968f
Show file tree
Hide file tree
Showing 44 changed files with 1,168 additions and 284 deletions.
4 changes: 4 additions & 0 deletions BackEnd/administration/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ def year_filter(self, queryset, name, value):
except ValueError:
raise ValidationError({name: [f"Enter a valid {name}. Use YYYY"]})
return queryset.filter(created_at__year=value)


class MonthlyProfileFilter(FilterSet):
year = filters.NumberFilter()
8 changes: 8 additions & 0 deletions BackEnd/administration/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,11 @@ class StatisticsSerializer(serializers.Serializer):
investors_count = serializers.IntegerField()
startups_count = serializers.IntegerField()
blocked_companies_count = serializers.IntegerField()


class MonthlyProfileStatisticsSerializer(serializers.Serializer):
month = serializers.IntegerField()
year = serializers.IntegerField()
investors_count = serializers.IntegerField()
startups_count = serializers.IntegerField()
startup_investor_count = serializers.IntegerField()
102 changes: 102 additions & 0 deletions BackEnd/administration/tests/test_monthly_profile_statistics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from rest_framework.test import APITestCase
from rest_framework import status
from administration.factories import AdminUserFactory, AdminProfileFactory
from utils.unittest_helper import utc_datetime


class TestMonthlyProfileStatisticsStaff(APITestCase):
def setUp(self):
self.user = AdminUserFactory()
self.client.force_authenticate(self.user)
self.test_startup_user = AdminUserFactory(is_staff=False)
self.test_investor_user = AdminUserFactory(is_staff=False)
self.test_blocked_company_user = AdminUserFactory(is_staff=False)
self.startup_company = AdminProfileFactory(
person_id=self.test_startup_user.id, is_registered=False
)
self.startup_company.created_at = utc_datetime(2023, 12, 7)
self.startup_company.save()
self.investor_company = AdminProfileFactory(
person_id=self.test_investor_user.id, is_startup=False
)
self.investor_company.created_at = utc_datetime(2024, 5, 10)
self.investor_company.save()
self.blocked_company = AdminProfileFactory(
person_id=self.test_blocked_company_user.id, status="blocked"
)
self.blocked_company.created_at = utc_datetime(2024, 12, 12)
self.blocked_company.save()

def test_get_monthly_profile_statistics(self):
response = self.client.get("/api/admin/profiles/statistics/monthly/")
data = [
{
"month": 12,
"year": 2023,
"investors_count": 0,
"startups_count": 1,
"startup_investor_count": 0,
},
{
"month": 5,
"year": 2024,
"investors_count": 1,
"startups_count": 0,
"startup_investor_count": 0,
},
{
"month": 12,
"year": 2024,
"investors_count": 0,
"startups_count": 0,
"startup_investor_count": 1,
},
]
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, data)

def test_get_monthly_statistics_incorrect_year(self):
response = self.client.get(
"/api/admin/profiles/statistics/monthly/?year=2002"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [])

def test_get_monthly_statistics_correct_year(self):
response = self.client.get(
"/api/admin/profiles/statistics/monthly/?year=2024"
)
data = [
{
"month": 5,
"year": 2024,
"investors_count": 1,
"startups_count": 0,
"startup_investor_count": 0,
},
{
"month": 12,
"year": 2024,
"investors_count": 0,
"startups_count": 0,
"startup_investor_count": 1,
},
]
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, data)


class TestMonthlyProfileStatisticsNotStaff(APITestCase):
def setUp(self):
self.user = AdminUserFactory(is_staff=False)
self.client.force_authenticate(self.user)

def test_get_profile_statistics(self):
response = self.client.get("/api/admin/profiles/statistics/monthly/")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class TestMonthlyProfileStatisticsUnauthorized(APITestCase):
def test_get_profile_statistics(self):
response = self.client.get("/api/admin/profiles/statistics/monthly/")
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
6 changes: 6 additions & 0 deletions BackEnd/administration/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
ProfilesListView,
ProfileDetailView,
ProfileStatisticsView,
MonthlyProfileStatisticsView,
UsersListView,
UserDetailView,
AutoModerationHoursView,
Expand All @@ -27,6 +28,11 @@
ProfileStatisticsView.as_view(),
name="profile-statistics",
),
path(
"profiles/statistics/monthly/",
MonthlyProfileStatisticsView.as_view(),
name="profile-statistics-monthly",
),
path("profiles/<pk>/", ProfileDetailView.as_view(), name="profile-detail"),
path(
"automoderation/",
Expand Down
42 changes: 39 additions & 3 deletions BackEnd/administration/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from django.db.models.functions import Concat
from django.db.models import F, Value, CharField
from django.db.models.functions import (
Concat,
TruncMonth,
ExtractMonth,
ExtractYear,
)
from django.db.models import F, Value, CharField, Count, Q
from django.http import JsonResponse
from django.views import View
from django.db.models import Count, Q
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.types import OpenApiTypes

Expand Down Expand Up @@ -33,6 +37,7 @@
CategoriesListSerializer,
CategoryDetailSerializer,
StatisticsSerializer,
MonthlyProfileStatisticsSerializer,
)
from administration.pagination import ListPagination
from administration.models import AutoModeration, ModerationEmail
Expand All @@ -46,6 +51,7 @@
CategoriesFilter,
ProfilesFilter,
ProfileStatisticsFilter,
MonthlyProfileFilter,
)
from utils.administration.send_email_notification import send_email_to_user

Expand Down Expand Up @@ -165,6 +171,36 @@ def get_object(self):
)


class MonthlyProfileStatisticsView(ListAPIView):
permission_classes = [IsStaffUser]
serializer_class = MonthlyProfileStatisticsSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = MonthlyProfileFilter

def get_queryset(self):
queryset = (
Profile.objects.annotate(
month_datetime=TruncMonth("created_at"),
month=ExtractMonth("created_at"),
year=ExtractYear("created_at"),
)
.values("month", "year")
.annotate(
investors_count=Count(
"pk", filter=Q(is_registered=True, is_startup=False)
),
startups_count=Count(
"pk", filter=Q(is_startup=True, is_registered=False)
),
startup_investor_count=Count(
"pk", filter=Q(is_startup=True, is_registered=True)
),
)
.order_by("month_datetime")
)
return queryset


@extend_schema(
request=AutoModerationHoursSerializer,
responses={
Expand Down
63 changes: 33 additions & 30 deletions BackEnd/profiles/tests/test_ordering.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,66 @@ class TestProfileOrdering(APITestCase):
def setUp(self) -> None:
self.user = UserFactory()

self.company_retail = ProfileCompanyFactory(
name="Retail company", completeness=105
)
self.company_retail.created_at = utc_datetime(2023, 12, 7)
self.company_retail.save()

self.company_winery = ProfileCompanyFactory(
name="Winery", completeness=1
)
self.company_winery.created_at = utc_datetime(2023, 12, 5)
self.company_winery.save()

self.company_delivery = ProfileCompanyFactory(
name="Delivery company", completeness=2
self.startup_brewery = ProfileStartupFactory(
name="Brewery", completeness=110
)
self.company_delivery.created_at = utc_datetime(2024, 1, 15)
self.company_delivery.save()
self.startup_brewery.created_at = utc_datetime(2023, 11, 1)
self.startup_brewery.save()

self.startup_catering = ProfileStartupFactory(
name="Catering service", completeness=5
)
self.startup_catering.created_at = utc_datetime(2023, 11, 15)
self.startup_catering.save()

self.startup_brewery = ProfileStartupFactory(
name="Brewery", completeness=110
self.company_winery = ProfileCompanyFactory(
name="Winery", completeness=1
)
self.startup_brewery.created_at = utc_datetime(2023, 11, 1)
self.startup_brewery.save()
self.company_winery.created_at = utc_datetime(2023, 12, 5)
self.company_winery.save()

self.company_retail = ProfileCompanyFactory(
name="Retail company", completeness=105
)
self.company_retail.created_at = utc_datetime(2023, 12, 7)
self.company_retail.save()

self.startup_bakery = ProfileStartupFactory(
name="Bakery", completeness=1
)
self.startup_bakery.created_at = utc_datetime(2023, 12, 31)
self.startup_bakery.save()

self.company_delivery = ProfileCompanyFactory(
name="Delivery company", completeness=2
)
self.company_delivery.created_at = utc_datetime(2024, 1, 15)
self.company_delivery.save()

self.saved_company_first = SavedCompanyFactory(
user=self.user,
company=self.company_delivery,
added_at=(timezone.now() - timedelta(days=3)),
)
self.saved_company_first.added_at = timezone.now() - timedelta(days=3)
self.saved_company_first.save()

self.saved_company_second = SavedCompanyFactory(
user=self.user,
company=self.company_winery,
added_at=(timezone.now() - timedelta(days=2)),
user=self.user, company=self.company_winery
)
self.saved_company_second.added_at = timezone.now() - timedelta(days=2)
self.saved_company_second.save()

self.saved_startup_third = SavedStartupFactory(
user=self.user,
company=self.startup_bakery,
added_at=(timezone.now() - timedelta(days=1)),
user=self.user, company=self.startup_bakery
)
self.saved_startup_third.added_at = timezone.now() - timedelta(days=1)
self.saved_startup_third.save()

self.saved_startup_fourth = SavedStartupFactory(
user=self.user,
company=self.startup_catering,
added_at=timezone.now(),
user=self.user, company=self.startup_catering
)
self.saved_startup_fourth.added_at = timezone.now()
self.saved_startup_fourth.save()

def test_get_list_of_profiles_alphabetical_order_asc(self):
response = self.client.get(
Expand Down
5 changes: 2 additions & 3 deletions FrontEnd/src/components/Header/Navbar/BurgerMenu.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react';
import Hamburger from 'hamburger-react';
import Menu from './Menu';
import SearchBox from './SearchBox';
import { useBurgerMenu } from '../../../context/BurgerMenuContext';
import css from './BurgerMenu.module.css';

const BurgerMenu = () => {
const { isOpen, toggleMenu } = useBurgerMenu();
const { isOpen, toggleMenu, menuRef } = useBurgerMenu();

return (
<div className={css.burgerMenuContainer}>
<div className={css.burgerMenuContainer} ref={menuRef}>
<Hamburger toggled={isOpen} toggle={toggleMenu} size={30} className={css.hamburgerIcon} />
<div className={`${css.menu} ${isOpen ? css.open : ''}`}>
{isOpen && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
.admin-submit__container {
margin-top: 16px;
display: flex;
justify-content: center;
}

.admin-submit__button {
display: block;
width: 100%;
border-radius: 4px;
border: 1px solid var(--main-button-color);
background: var(--main-button-color);
box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.04);
color: var(--light-button-text-color);
color: var(--main-button-text-color);
font-feature-settings: 'calt' off;
font-style: normal;
padding: 5px 15px 5px 15px;
padding: 8px 15px;
font-family: var(--font-main);
font-size: 16px;
font-weight: 600;
Expand All @@ -22,6 +20,10 @@
cursor: pointer;
}

.admin-submit__button:hover {
background: var(--main-button-hover-color);
}

.admin-submit__button:active {
transform: translateY(2px);
}
Loading

0 comments on commit d8f968f

Please sign in to comment.