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

Улучшение уведомлений, тесты уведомлений. #41

Merged
merged 6 commits into from
Jan 31, 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ TO-DO: Описание проекта.
| [Django filter](https://pypi.org/project/django-filter/) | Библиотека для фильтрации данных в приложениях Django. |
| [Django Notifications](https://github.com/django-notifications/django-notifications) | Уведомления. |
| [django-cors-headers](https://pypi.org/project/django-cors-headers/) | Что-то делает с headers |
| [django-dirtyfields](https://django-dirtyfields.readthedocs.io/en/stable/quickstart.html) | Для доступа к измененной информации в джанго сигнале `post_save` |
| [Flake8](https://pypi.org/project/flake8/), [black](https://pypi.org/project/black/), [isort](https://pypi.org/project/isort/), [Pre-commit](https://pypi.org/project/pre-commit/) | Инструменты для поддержания Code-Style в проекте. |

## Code-Style при разработке
Expand Down
Empty file added api/v1/tests/__init__.py
Empty file.
106 changes: 106 additions & 0 deletions api/v1/tests/tests_notifications_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from django.utils import timezone
from notifications.models import Notification
from rest_framework import status
from rest_framework.test import APIClient, APITestCase

from core.statuses import Status
from ipr.models import IPR

User = get_user_model()


class NotificationAPITestCase(APITestCase):
def setUp(self):
"""Тестовые данные."""
# ---------------------------------------------------------Пользователи
self.creator = User.objects.create_user(
username="creator",
email="[email protected]",
password="password",
)
self.executor = User.objects.create_user(
username="executor",
email="[email protected]",
password="password",
)
self.client = APIClient()
self.client.force_authenticate(user=self.executor)
# ----------------------------------------------------------------ИПР-1
self.ipr1 = IPR.objects.create(
title="Test IPR",
creator=self.creator,
creation_date=timezone.now(),
start_date=timezone.now() + timezone.timedelta(days=1),
end_date=timezone.now() + timezone.timedelta(days=2),
status=Status.IN_PROGRESS,
executor=self.executor,
)
self.ipr_content_type_id = ContentType.objects.get_for_model(
self.ipr1
).id
self.ipr_notification = Notification.objects.filter(
target_object_id=self.ipr1.id,
target_content_type_id=self.ipr_content_type_id,
).first()
# ----------------------------------------------------------------ИПР-2
self.ipr2 = IPR.objects.create(
title="Test IPR",
creator=self.creator,
creation_date=timezone.now(),
start_date=timezone.now() + timezone.timedelta(days=1),
end_date=timezone.now() + timezone.timedelta(days=2),
status=Status.IN_PROGRESS,
executor=self.executor,
)
self.ipr2_content_type_id = ContentType.objects.get_for_model(
self.ipr2
).id
self.ipr2_notification = Notification.objects.filter(
target_object_id=self.ipr2.id,
target_content_type_id=self.ipr2_content_type_id,
).first()
# ------------------------------------------------------------Константы
self.AMOUNT_OF_NOTIFICATIONS = 2
# ------------------------------------------------------------Эндпоинты
self.all_notifications_url = reverse("notifications-list")
self.one_notification_url = reverse(
"notifications-detail",
kwargs={"pk": self.ipr_notification.id},
)
self.notifications_mark_all_as_read_url = reverse(
"notifications-mark-all-as-read"
)
self.notifications_unseen_count_url = reverse("notifications-unseen")

def test_list_notifications(self):
"""Тест эндпоинта уведомлений."""
response = self.client.get(self.all_notifications_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), self.AMOUNT_OF_NOTIFICATIONS)

def test_mark_notification_as_read(self):
"""Тест эндпоинта отметки уведомления как прочтенного."""
self.assertTrue(self.ipr_notification.unread)
response = self.client.patch(
self.one_notification_url, {"unread": False}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.ipr_notification.refresh_from_db()
self.assertFalse(self.ipr_notification.unread)

def test_mark_all_notifications_as_read(self):
"""Тест эндпоинта отметки всех уведомлений пользователя
как прочтенных."""
self.assertTrue(self.executor.notifications.unread().count() > 0)
response = self.client.get(self.notifications_mark_all_as_read_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(self.executor.notifications.unread().count(), 0)

def test_unseen_notifications_count(self):
"""Тест эндпоинта получения количества непрочитанных уведомлений."""
response = self.client.get(self.notifications_unseen_count_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["unseen"], self.AMOUNT_OF_NOTIFICATIONS)
2 changes: 1 addition & 1 deletion api/v1/views/notifications_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
class NotificationViewSet(viewsets.ModelViewSet):
serializer_class = NotificationSerializer
http_method_names = ["get", "patch", "head", "options"]
http_method_names = ("get", "patch", "head", "options")

def partial_update(self, request, pk):
"""Отметить уведомление как прочитанное."""
Expand Down
10 changes: 9 additions & 1 deletion config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@
)
USE_POSTGRESQL = env.bool("USE_POSTGRESQL", default=False)
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["localhost", "127.0.0.1"])
ALLOWED_HOSTS = env.list(
"ALLOWED_HOSTS",
default=[
"localhost",
"127.0.0.1",
"[::1]",
"testserver",
],
)
DB_NAME = env.str("DB_NAME", default="IPR")
DB_USER = env.str("POSTGRES_USER", default="username")
DB_PASSWORD = env.str("POSTGRES_PASSWORD", default="smart-password123")
Expand Down
66 changes: 44 additions & 22 deletions core/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,69 @@
from django.dispatch import receiver
from notifications.signals import notify

from core.statuses import Status
from ipr.models import IPR
from tasks.models import Task


@receiver(post_save, sender=IPR)
def created_ipr_notification(sender, instance: IPR, created, **kwargs):
"""Создание уведомления о новом ИПР."""
msg = None
if created:
# TODO: Улучшить текст
text = "Вам назначен новый ИПР"
msg = f"Вам назначен новый ИПР: {instance.title}"
notify.send(
sender=instance.creator,
recipient=instance.executor,
verb=text,
target=instance,
)
else:
# TODO: Улучшить текст
text = "Изменение в ИПР."
notify.send(
sender=instance.creator,
recipient=instance.executor,
verb=text,
verb=msg,
target=instance,
)
elif instance.is_dirty(): # если ИПР изменился
old_data = instance.get_dirty_fields()
# ------------------------------------------Уведомления для сотрудника
if "start_date" in old_data:
msg = (
f'Дата начала работы по ИПР "{instance.title}"'
f" изменена на {instance.start_date}"
)
notify.send(
sender=instance.creator,
recipient=instance.executor,
verb=msg,
target=instance,
)
elif "end_date" in old_data:
msg = (
f'Дата окончания работы по ИПР "{instance.title}"'
f" изменена на {instance.end_date}"
)
notify.send(
sender=instance.creator,
recipient=instance.executor,
verb=msg,
target=instance,
)
# ----------------------------------------Уведомления для руководителя
elif instance.status is Status.COMPLETE:
msg = (
f"{instance.executor.get_full_name()}"
f" закрыл ИПР: {instance.title}"
)
notify.send(
sender=instance.executor,
recipient=instance.creator,
verb=msg,
target=instance,
)


@receiver(post_save, sender=Task)
def created_task_notification(sender, instance: Task, created, **kwargs):
"""Создание уведомления о новой задачи."""
"""Создание уведомления о новой задаче."""

if created:
text = "Вам добавлена новая задача"
notify.send(
sender=instance.creator,
recipient=instance.executor,
verb=text,
target=instance,
)
else:
text = "Изменение в задаче."
task_name = getattr(instance, "name", "")
text = f"Вам добавлена новая задача: {task_name}"
notify.send(
sender=instance.creator,
recipient=instance.executor,
Expand Down
Empty file added core/tests/__init__.py
Empty file.
151 changes: 151 additions & 0 deletions core/tests/tests_notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.utils import timezone
from notifications.models import Notification

from core.statuses import Status
from ipr.models import IPR
from tasks.models import Task

User = get_user_model()


class NotificationsTest(TestCase):
def setUp(self):
"""Тестовые данные."""
# ---------------------------------------------------------Пользователи
self.creator = User.objects.create_user(
username="creator",
email="[email protected]",
password="password",
)
self.executor = User.objects.create_user(
username="executor",
email="[email protected]",
password="password",
)
# ------------------------------------------------------------------ИПР
self.ipr = IPR.objects.create(
title="Test IPR",
creator=self.creator,
creation_date=timezone.now(),
start_date=timezone.now() + timezone.timedelta(days=1),
end_date=timezone.now() + timezone.timedelta(days=2),
status=Status.IN_PROGRESS,
executor=self.executor,
)
self.ipr_content_type_id = ContentType.objects.get_for_model(
self.ipr
).id
self.ipr_notification = Notification.objects.filter(
target_object_id=self.ipr.id,
target_content_type_id=self.ipr_content_type_id,
).first()
# ---------------------------------------------------------------Задача
self.task = Task.objects.create(
name="Test Task",
creator=self.creator,
executor=self.executor,
creation_date=timezone.now(),
start_date=timezone.now() + timezone.timedelta(days=1),
end_date=timezone.now() + timezone.timedelta(days=2),
)
self.task_content_type_id = ContentType.objects.get_for_model(
self.task
).id
self.task_notification = Notification.objects.filter(
target_object_id=self.task.id,
target_content_type_id=self.task_content_type_id,
).first()
# ------------------------------------------------------------Константы
self.AMOUNT_OF_NOTIFICATIONS = 2

def test_notifications_created(self):
"""Проверка количества созданный уведомлений."""
self.assertEqual(
Notification.objects.count(), self.AMOUNT_OF_NOTIFICATIONS
)

def test_created_ipr_notification(self):
"""Тест создания уведомления о новом ИПР."""
self.assertIsNotNone(
self.ipr_notification, "Объект уведомления не найден"
)
self.assertEqual(
self.ipr_notification.recipient,
self.executor,
"Не правильный получатель",
)
self.assertEqual(
self.ipr_notification.verb,
f"Вам назначен новый ИПР: {self.ipr.title}",
"Не правильное содержание уведомления",
)

def test_created_task_notification(self):
"""Тест создания уведомления о новой задаче."""
self.assertIsNotNone(self.task_notification, "Объект задачи не найден")
self.assertEqual(
self.task_notification.recipient,
self.executor,
"Не правильный получатель",
)
self.assertEqual(
self.task_notification.verb,
f"Вам добавлена новая задача: {self.task.name}",
"Не правильное содержание уведомления",
)

def test_edited_ipr_notification(self):
"""Тест создания уведомления об изменениях в начальной дате ИПР."""

new_start_date = timezone.now() + timezone.timedelta(days=5)
self.ipr.start_date = new_start_date
self.ipr.save()
self.ipr.refresh_from_db()
expected_msg = (
f'Дата начала работы по ИПР "{self.ipr.title}"'
f" изменена на {self.ipr.start_date}"
)
self.assertEqual(
self.ipr.verb,
expected_msg,
(
"Не правильное содержание уведомления"
" об изменении start_date в ИПР"
),
)

def test_edited_ipr_end_date_notification(self):
"""Тест создания уведомления об изменении даты окончания IPR."""

new_end_date = timezone.now() + timezone.timedelta(days=10)
self.ipr.end_date = new_end_date
self.ipr.save()
self.ipr.refresh_from_db()
expected_msg = (
f'Дата окончания работы по ИПР "{self.ipr.title}"'
f" изменена на {self.ipr.end_date}"
)
self.assertEqual(
self.ipr.verb,
expected_msg,
"Не правильное содержание уведомления об изменении end_date в ИПР",
)

def test_notification_created_after_ipr_complete(self):
"""Тест создания уведомления о завершении ИПР."""

self.ipr.status = Status.COMPLETE
self.ipr.save()
self.ipr.refresh_from_db()
expected_msg = (
f"{self.ipr.executor.get_full_name()}"
f" закрыл ИПР: {self.ipr.title}"
)
self.assertEqual(
self.ipr.verb,
expected_msg,
"Не правильное содержание уведомления о завершении ИПР",
)
6 changes: 0 additions & 6 deletions docs/Makefile

This file was deleted.

Loading
Loading