view and model notif and news
This commit is contained in:
@@ -134,4 +134,15 @@ class SecurityBreachAttemptAdmin(ModelAdmin, ImportExportModelAdmin):
|
|||||||
|
|
||||||
return format_html(
|
return format_html(
|
||||||
svg
|
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):
|
def get_location_from_ip(ip_address):
|
||||||
try:
|
try:
|
||||||
response = requests.get(f"http://ip-api.com/json/{ip_address}")
|
response = requests.get(f"http://ip-api.com/json/{ip_address}")
|
||||||
|
|||||||
@@ -32,3 +32,32 @@ class PushSubscriptionSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PushSubscription
|
model = PushSubscription
|
||||||
fields = ('endpoint', 'keys')
|
fields = ('endpoint', 'keys')
|
||||||
|
|
||||||
|
|
||||||
|
class NewsSerializer(serializers.ModelSerializer):
|
||||||
|
notif_type = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = NewsModel
|
||||||
|
fields = ['title', 'content', 'image', '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', 'image', '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('unsubscribe', views.UnsubscribeView.as_view(), name='unsubscibe'),
|
||||||
path('attack/view/<int:pk>', views.ChangeViewAttack.as_view(), name='attack-view'),
|
path('attack/view/<int:pk>', views.ChangeViewAttack.as_view(), name='attack-view'),
|
||||||
path('logout', views.LogoutView.as_view(), name='logout'),
|
path('logout', views.LogoutView.as_view(), name='logout'),
|
||||||
|
path('notif/list', views.NotificationListAPIView.as_view(), name='notif-list')
|
||||||
]
|
]
|
||||||
@@ -14,6 +14,9 @@ from django.views import View
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
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):
|
class SendOTPView(APIView):
|
||||||
@@ -268,4 +271,73 @@ from djoser.urls.jwt import views as djoser_jwt_views
|
|||||||
post=extend_schema(tags=["authentication"])
|
post=extend_schema(tags=["authentication"])
|
||||||
)
|
)
|
||||||
class TokenVerifyView(djoser_jwt_views.TokenVerifyView):
|
class TokenVerifyView(djoser_jwt_views.TokenVerifyView):
|
||||||
pass
|
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(val):
|
||||||
|
return True if val and val == 'true' else False
|
||||||
|
|||||||
@@ -221,6 +221,18 @@ UNFOLD = {
|
|||||||
"link": reverse_lazy("admin:account_securitybreachattemptmodel_changelist"),
|
"link": reverse_lazy("admin:account_securitybreachattemptmodel_changelist"),
|
||||||
"badge": 'utils.admin.new_attck_count'
|
"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