merge
This commit is contained in:
@@ -135,3 +135,14 @@ class SecurityBreachAttemptAdmin(ModelAdmin, ImportExportModelAdmin):
|
||||
return format_html(
|
||||
svg
|
||||
)
|
||||
|
||||
|
||||
@admin.register(NewsModel)
|
||||
class NewsAdmin(ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(UserNotificationModel)
|
||||
class UserNotificationAdmin(ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 5.1.2 on 2025-04-24 22:25
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0025_alter_pushsubscription_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='News',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=50)),
|
||||
('content', models.TextField()),
|
||||
('image', models.ImageField(upload_to='')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserNotification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=50)),
|
||||
('content', models.TextField()),
|
||||
('image', models.ImageField(upload_to='')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_read', models.BooleanField(default=False)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.1.2 on 2025-04-24 22:32
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0026_news_usernotification'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='News',
|
||||
new_name='NewsModel',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='UserNotification',
|
||||
new_name='UserNotificationModel',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.2 on 2025-05-24 17:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0027_rename_news_newsmodel_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='newsmodel',
|
||||
name='is_read',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -207,6 +207,32 @@ class PushSubscription(models.Model):
|
||||
|
||||
|
||||
|
||||
class NotifBaseModel(models.Model):
|
||||
title = models.CharField(max_length=50)
|
||||
content = models.TextField()
|
||||
image = models.ImageField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
is_read = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class NewsModel(NotifBaseModel):
|
||||
|
||||
def notif_type(self):
|
||||
return 'NEWS'
|
||||
|
||||
|
||||
class UserNotificationModel(NotifBaseModel):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
def notif_type(self):
|
||||
return 'USER_NOTIF'
|
||||
|
||||
|
||||
def get_location_from_ip(ip_address):
|
||||
try:
|
||||
response = requests.get(f"http://ip-api.com/json/{ip_address}")
|
||||
|
||||
@@ -32,3 +32,32 @@ class PushSubscriptionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PushSubscription
|
||||
fields = ('endpoint', 'keys')
|
||||
|
||||
|
||||
class NewsSerializer(serializers.ModelSerializer):
|
||||
notif_type = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = NewsModel
|
||||
fields = ['title', 'content', 'created_at', 'notif_type']
|
||||
|
||||
def get_notif_type(self, obj):
|
||||
return 'NEWS'
|
||||
|
||||
class UserNotifSerializer(serializers.ModelSerializer):
|
||||
notif_type = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = UserNotificationModel
|
||||
fields = ['title', 'content', 'created_at', 'notif_type', 'is_read']
|
||||
|
||||
def get_notif_type(self, obj):
|
||||
return 'USER_NOTIF'
|
||||
|
||||
|
||||
class UnifiedNotifSerializer(serializers.Serializer):
|
||||
def to_representation(self, instance):
|
||||
if isinstance(instance, NewsModel):
|
||||
return NewsSerializer(instance).data
|
||||
elif isinstance(instance, UserNotificationModel):
|
||||
return UserNotifSerializer(instance).data
|
||||
|
||||
@@ -17,4 +17,5 @@ urlpatterns = [
|
||||
path('unsubscribe', views.UnsubscribeView.as_view(), name='unsubscibe'),
|
||||
path('attack/view/<int:pk>', views.ChangeViewAttack.as_view(), name='attack-view'),
|
||||
path('logout', views.LogoutView.as_view(), name='logout'),
|
||||
path('notification/all', views.NotificationListAPIView.as_view(), name='notif-list')
|
||||
]
|
||||
@@ -14,6 +14,9 @@ from django.views import View
|
||||
from rest_framework import serializers
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from itertools import chain
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
|
||||
from utils.pagination import StructurePagination
|
||||
|
||||
|
||||
class SendOTPView(APIView):
|
||||
@@ -269,3 +272,72 @@ from djoser.urls.jwt import views as djoser_jwt_views
|
||||
)
|
||||
class TokenVerifyView(djoser_jwt_views.TokenVerifyView):
|
||||
pass
|
||||
|
||||
class NotificationListAPIView(APIView):
|
||||
serializer_class = UnifiedNotifSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
pagination_class = StructurePagination
|
||||
@extend_schema(
|
||||
parameters=[
|
||||
|
||||
|
||||
OpenApiParameter(
|
||||
name="notif_type",
|
||||
type=OpenApiTypes.STR,
|
||||
description="NEWS OR USER_NOTIF",
|
||||
required=False,
|
||||
),
|
||||
|
||||
OpenApiParameter(
|
||||
name="is_read",
|
||||
type=OpenApiTypes.STR,
|
||||
description="is_read filter",
|
||||
required=False,
|
||||
),
|
||||
|
||||
OpenApiParameter(
|
||||
name="limit",
|
||||
description="Number of results to return per page (pagination).",
|
||||
required=False,
|
||||
type=OpenApiTypes.INT,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="offset",
|
||||
description="The starting position of the results (pagination).",
|
||||
required=False,
|
||||
type=OpenApiTypes.INT,
|
||||
),
|
||||
],
|
||||
)
|
||||
def get(self, request):
|
||||
notif_type = request.query_params.get('notif_type')
|
||||
is_read_param = request.query_params.get('is_read')
|
||||
is_read = self.str_to_bool(is_read_param)
|
||||
|
||||
news_qs = NewsModel.objects.all()
|
||||
user_notif_qs = UserNotificationModel.objects.filter(user=request.user)
|
||||
|
||||
if is_read is not None:
|
||||
news_qs = news_qs.filter(is_read=is_read)
|
||||
user_notif_qs = user_notif_qs.filter(is_read=is_read)
|
||||
|
||||
if notif_type is None:
|
||||
notifs = sorted(
|
||||
chain(news_qs, user_notif_qs),
|
||||
key=lambda n: n.created_at,
|
||||
reverse=True
|
||||
)
|
||||
elif notif_type == 'NEWS':
|
||||
notifs = news_qs
|
||||
elif notif_type == 'USER_NOTIF':
|
||||
notifs = user_notif_qs
|
||||
else:
|
||||
return Response({'detail': 'wrong notif type'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
paginator = self.pagination_class()
|
||||
paginated_notifs = paginator.paginate_queryset(notifs, request)
|
||||
serializer = self.serializer_class(paginated_notifs, many=True)
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
|
||||
def str_to_bool(self, val):
|
||||
return True if val and val == 'read' else False
|
||||
|
||||
@@ -221,6 +221,18 @@ UNFOLD = {
|
||||
"link": reverse_lazy("admin:account_securitybreachattemptmodel_changelist"),
|
||||
"badge": 'utils.admin.new_attck_count'
|
||||
},
|
||||
{
|
||||
"title": _("اخبار"),
|
||||
"icon": "newspaper",
|
||||
"link": reverse_lazy("admin:account_newsmodel_changelist"),
|
||||
|
||||
},
|
||||
{
|
||||
"title": _("اطلاعیه ها"),
|
||||
"icon": "notifications",
|
||||
"link": reverse_lazy("admin:account_usernotificationmodel_changelist"),
|
||||
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user