Skip to content

Commit

Permalink
resolve conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
Lvyshnevska committed Dec 9, 2024
2 parents 353eedc + 010281f commit 60855ff
Show file tree
Hide file tree
Showing 11 changed files with 463 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Нове повідомлення</title>
<style>
body {
font-family: Arial, sans-serif;
color: black;
}
.email-container {
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
.email-footer {
margin-top: 20px;
font-size: 12px;
color: gray;
}
img {
max-width: 150px;
}
.email-body {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="email-container">
<img src="{{ logo_url }}" alt="CraftMerge Logo" />
<div class="email-body">
<p>Доброго дня, {{ user_name }}!</p>
<p>Ви отримали нове повідомлення:</p>
<p><b>Категорія:</b> {{ category }}</p>
<p><b>Повідомлення:</b></p>
<p>{{ message }}</p>
</div>
<div class="email-footer">
<p>З повагою,</p>
<p>Команда CraftMerge</p>
</div>
</div>
</body>
</html>
109 changes: 109 additions & 0 deletions BackEnd/administration/tests/test_send_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from django.core import mail
from django.conf import settings
from rest_framework import status
from rest_framework.test import APITestCase
from authentication.factories import UserFactory
from utils.administration.send_email_notification import send_email_to_user


class TestSendMessageView(APITestCase):
def setUp(self):
self.admin = UserFactory(is_staff=True, is_active=True)
self.user = UserFactory(is_active=True)
self.client.force_authenticate(self.admin)
self.url = f"/api/admin/users/{self.user.id}/send_message/"

def test_send_message_success(self):
data = {
"email": self.user.email,
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_send_message_invalid_data(self):
data = {
"email": self.user.email,
"category": "Iнше",
"message": "Short",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_send_message_unauthorized(self):
self.client.logout()
data = {
"email": self.user.email,
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_send_message_user_not_found(self):
url = "/api/admin/users/9999/send_message/"
data = {
"email": "nonexistent@test.com",
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)


class TestSendEmailFunctionality(APITestCase):
def setUp(self):
self.user = UserFactory(
name="Test", surname="User", email="test_user@example.com"
)

def send_email(self, category, message_content, email=None):
email = email if email else self.user.email
return send_email_to_user(
user=self.user,
category=category,
message_content=message_content,
email=email,
)

def test_send_email_success(self):
self.send_email(
category="Інше",
message_content="This is a test message.",
email="test_user@example.com",
)

self.assertEqual(len(mail.outbox), 1)
email = mail.outbox[0]
self.assertEqual(email.subject, "Адміністратор CraftMerge - Інше")
self.assertIn("This is a test message.", email.body)
self.assertEqual(email.to, ["test_user@example.com"])
self.assertEqual(email.from_email, settings.EMAIL_HOST_USER)

def test_send_email_empty_message(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="Інше",
message_content="",
email="test_user@example.com",
)
self.assertEqual(str(e.exception), "Message content cannot be empty.")

def test_send_email_invalid_email(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="Інше",
message_content="Test message",
email="invalid_email",
)
self.assertEqual(str(e.exception), "Invalid email address.")

def test_send_email_missing_category(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="",
message_content="Test message",
email="test_user@example.com",
)
self.assertEqual(str(e.exception), "Category is required.")
6 changes: 6 additions & 0 deletions BackEnd/administration/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ModerationEmailView,
FeedbackView,
CreateAdminUserView,
SendMessageView,
)

app_name = "administration"
Expand All @@ -28,4 +29,9 @@
path("contacts/", ContactsView.as_view(), name="contacts"),
path("feedback/", FeedbackView.as_view(), name="feedback"),
path("admin_create/", CreateAdminUserView.as_view(), name="admin-create"),
path(
"users/<pk>/send_message/",
SendMessageView.as_view(),
name="send-message",
),
]
41 changes: 40 additions & 1 deletion BackEnd/administration/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
OpenApiExample,
OpenApiResponse,
)

from rest_framework.generics import (
ListAPIView,
RetrieveUpdateDestroyAPIView,
Expand All @@ -30,6 +29,7 @@
from .permissions import IsStaffUser, IsStaffUserOrReadOnly, IsSuperUser
from .serializers import FeedbackSerializer
from utils.administration.send_email_feedback import send_email_feedback
from utils.administration.send_email_notification import send_email_to_user

from django_filters.rest_framework import DjangoFilterBackend
from .filters import UsersFilter
Expand Down Expand Up @@ -199,3 +199,42 @@ def perform_create(self, serializer):
category = serializer.validated_data["category"]

send_email_feedback(email, message, category)


class SendMessageView(CreateAPIView):
"""
API endpoint for sending a custom email message to a specific user.
This view allows administrators to send a message to a user's registered email.
It validates the request payload, retrieves the user based on the provided ID,
and sends the email using the specified category and message content.
"""

queryset = CustomUser.objects.all()
permission_classes = [IsStaffUser]
serializer_class = FeedbackSerializer

def perform_create(self, serializer):
"""
Handles the email sending logic after successful validation.
This method is executed after the request data has been validated
by the serializer. It retrieves the user, validates their existence,
and sends the email with the provided category and message content.
Parameters:
serializer (FeedbackSerializer): The serializer instance containing
the validated data from the request.
"""
user = self.get_object()
email = serializer.validated_data["email"]
category = serializer.validated_data["category"]
message_content = serializer.validated_data["message"]

send_email_to_user(
user=user,
category=category,
message_content=message_content,
email=email,
sender_name="Адміністратор CraftMerge",
)
16 changes: 12 additions & 4 deletions BackEnd/search/tests/test_advanced_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ def setUp(self) -> None:

def test_get_search_in_all_companies_official_order_by_name(self):
response = self.client.get(path="/api/search/advanced?search=official")
names_from_response = [prof["name"] for prof in response.data["results"]]
names_from_response = [
prof["name"] for prof in response.data["results"]
]
self.assertEqual(200, response.status_code)
self.assertEqual(
names_from_response,
Expand Down Expand Up @@ -89,7 +91,9 @@ def test_get_search_name_service_info_upper_case(self):

def test_get_search_parcial_item(self):
response = self.client.get(path="/api/search/advanced?search=ch")
names_from_response = [prof["name"] for prof in response.data["results"]]
names_from_response = [
prof["name"] for prof in response.data["results"]
]
self.assertEqual(200, response.status_code)
self.assertEqual(3, response.data["total_items"])
self.assertEqual(
Expand All @@ -108,7 +112,9 @@ def test_get_search_name_product_info(self):

def test_get_search_name_service_product_common_info(self):
response = self.client.get(path="/api/search/advanced?search=KYIV")
names_from_response = [prof["name"] for prof in response.data["results"]]
names_from_response = [
prof["name"] for prof in response.data["results"]
]
self.assertEqual(200, response.status_code)
self.assertEqual(2, response.data["total_items"])
self.assertEqual(names_from_response, ["Kryvyi Rig", "Kyiv"])
Expand All @@ -117,7 +123,9 @@ def test_get_search_devide_item(self):
response = self.client.get(
path="/api/search/advanced?search=product info"
)
names_from_response = [prof["name"] for prof in response.data["results"]]
names_from_response = [
prof["name"] for prof in response.data["results"]
]
self.assertEqual(200, response.status_code)
self.assertEqual(1, response.data["total_items"])
self.assertEqual(names_from_response, ["Kryvyi Rig"])
Expand Down
8 changes: 2 additions & 6 deletions BackEnd/search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
class SearchCompanyView(ListAPIView):
queryset = (
Profile.objects.active_only()
.prefetch_related(
"regions", "categories", "activities"
)
.prefetch_related("regions", "categories", "activities")
.order_by("id")
)
serializer_class = CompanySerializers
Expand All @@ -40,9 +38,7 @@ def get_serializer_context(self):
class AdvancedSearchView(ListAPIView):
queryset = (
Profile.objects.active_only()
.prefetch_related(
"regions", "categories", "activities"
)
.prefetch_related("regions", "categories", "activities")
.order_by("id")
)
serializer_class = CompanyAdvancedSerializers
Expand Down
58 changes: 58 additions & 0 deletions BackEnd/utils/administration/send_email_notification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
from django.core.validators import validate_email
from django.core.exceptions import ValidationError

EMAIL_CONTENT_SUBTYPE = "html"
PROTOCOL = "http"


def send_email_to_user(
user,
category,
message_content,
email=None,
sender_name="Адміністратор CraftMerge",
template_name="administration/admin_message_template.html",
):
"""
Sends an email message to the user using the specified template.
:param user: The user object (CustomUser)
:param category: The email category
:param message_content: The message content
:param email: (Optional) The recipient's email
:param sender_name: Name of the sender
:param template_name: The path to the HTML template
"""
if not category:
raise ValueError("Category is required.")
if not message_content.strip():
raise ValueError("Message content cannot be empty.")
try:
validate_email(email or user.email)
except ValidationError:
raise ValueError("Invalid email address.")

context = {
"user_name": f"{user.name} {user.surname}",
"message": message_content,
"category": category,
"sender_name": sender_name,
"logo_url": f"{PROTOCOL}://178.212.110.52/craftMerge-logo.png",
}

email_body = render_to_string(template_name, context)
recipient_email = email if email else user.email

subject = f"{sender_name} - {category}"

email = EmailMultiAlternatives(
subject=subject,
body=email_body,
from_email=settings.EMAIL_HOST_USER,
to=[recipient_email],
)
email.content_subtype = EMAIL_CONTENT_SUBTYPE
email.send(fail_silently=False)
Binary file added FrontEnd/public/craftMerge-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 60855ff

Please sign in to comment.