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

fix: optimization code #63

Merged
merged 1 commit into from
Dec 18, 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
22 changes: 22 additions & 0 deletions apps/mystories/migrations/0003_notification_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.0.8 on 2024-12-18 07:19

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("mystories", "0002_initial"),
]

operations = [
migrations.AddField(
model_name="notification",
name="type",
field=models.CharField(
choices=[("SINGLE", "Single"), ("ALL", "All")],
default="SINGLE",
max_length=10,
verbose_name="Type",
),
),
]
28 changes: 11 additions & 17 deletions apps/mystories/models/notification.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import io

from PIL import Image
from django.core.files.base import ContentFile
from django.db import models
from django.utils.translation import gettext_lazy as _

from apps.shared.models import AbstractBaseModel


class NotificationType(models.TextChoices):
SINGLE = "SINGLE", _("Single")
ALL = "ALL", _("All")


class Notification(AbstractBaseModel):
user = models.ForeignKey(
"users.User",
Expand All @@ -25,6 +26,12 @@ class Notification(AbstractBaseModel):
message = models.TextField(db_index=True, verbose_name=_("Message"))
is_send = models.BooleanField(default=False, verbose_name=_("Is Send"))
is_read = models.BooleanField(default=False, verbose_name=_("Is Read"))
type = models.CharField(
max_length=10,
choices=NotificationType,
default=NotificationType.SINGLE,
verbose_name=_("Type"),
)

class Meta:
db_table = "notifications"
Expand All @@ -34,16 +41,3 @@ class Meta:

def __str__(self):
return self.message

def save(self, *args, **kwargs):
if self.banner:
img = Image.open(self.banner)
if img.format != "WEBP":
img_io = io.BytesIO()
img.save(img_io, format="WEBP", quality=100)
self.banner.save(
f"{self.banner.name.split('.')[0]}.webp",
ContentFile(img_io.getvalue()),
save=False,
)
super().save(*args, **kwargs)
30 changes: 0 additions & 30 deletions apps/mystories/models/posts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import io

from PIL import Image
from django.core.files.base import ContentFile
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _

from apps.shared.models import AbstractBaseModel
Expand Down Expand Up @@ -59,30 +53,6 @@ def increment_views(self):
self.view_count += 1
self.save()

def save(self, *args, **kwargs):
if not self.created_at:
self.created_at = timezone.now()
if not self.slug:
self.slug = slugify(f"{self.title}-{self.created_at.strftime('%Y-%m-%d')}")
unique_slug = self.slug
num = 1
while Post.objects.filter(slug=unique_slug).exists():
unique_slug = f"{self.slug}-{num}"
num += 1
self.slug = unique_slug
if self.banner:
img = Image.open(self.banner)
if img.format != "WEBP":
img_io = io.BytesIO()
img.save(img_io, format="WEBP", quality=100)
self.banner.save(
f"{self.banner.name.split('.')[0]}.webp",
ContentFile(img_io.getvalue()),
save=False,
)
self.slug = self.slug.lower()
super().save(*args, **kwargs)


class Like(AbstractBaseModel):
post = models.ForeignKey(
Expand Down
25 changes: 22 additions & 3 deletions apps/mystories/signals/notification.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
import io
import logging

from PIL import Image
from django.core.files.base import ContentFile
from django.db.models.signals import post_save
from django.dispatch import receiver

from apps.mystories.models import Notification
from apps.mystories.tasks import send_notification_task
from apps.mystories.models import Notification, NotificationType
from apps.mystories.tasks import send_notification_task, send_notification_to_all_task

logger = logging.getLogger(__name__)


@receiver(post_save, sender=Notification)
def send_notification(sender, instance, created, **kwargs): # noqa
if created:
if created and instance.type == NotificationType.SINGLE:
send_notification_task.delay(instance.id)
logger.info(f"Notification {instance.title} sent.")
elif created and instance.type == NotificationType.ALL:
send_notification_to_all_task.delay(instance.id)
logger.info(f"Notification {instance.title} sent to all users.")
elif created:
if instance.banner:
img = Image.open(instance.banner)
if img.format != "WEBP":
img_io = io.BytesIO()
img.save(img_io, format="WEBP", quality=100)
instance.banner.save(
f"{instance.banner.name.split('.')[0]}.webp",
ContentFile(img_io.getvalue()),
save=False,
)
instance.save()
39 changes: 39 additions & 0 deletions apps/mystories/signals/posts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import io

from PIL import Image
from django.core.files.base import ContentFile
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.utils import timezone
from django.utils.text import slugify

from apps.mystories.models import Like, Comment, Saved
from apps.mystories.models import Post


@receiver(post_save, sender=Like)
Expand Down Expand Up @@ -41,3 +48,35 @@ def increment_saved_count(sender, instance, created, **kwargs): # noqa
def decrement_saved_count(sender, instance, **kwargs): # noqa
instance.post.saved_count -= 1
instance.post.save()


@receiver(post_save, sender=Post)
def post_save_post(sender, instance, created, **kwargs):
if created:
if not instance.created_at:
instance.created_at = timezone.now()
if not instance.slug:
instance.slug = slugify(
f"{instance.title}-{instance.created_at.strftime('%Y-%m-%d')}"
)
unique_slug = instance.slug
num = 1
while Post.objects.filter(slug=unique_slug).exists():
unique_slug = f"{instance.slug}-{num}"
num += 1
instance.slug = unique_slug
instance.slug = instance.slug.lower()
instance.save()
if instance.pk:
old_instance = Post.objects.get(pk=instance.pk)
if old_instance.banner != instance.banner:
if instance.banner:
img = Image.open(instance.banner)
if img.format != "WEBP":
img_io = io.BytesIO()
img.save(img_io, format="WEBP", quality=100)
instance.banner.save(
f"{instance.banner.name.split('.')[0]}.webp",
ContentFile(img_io.getvalue()),
save=False,
)
35 changes: 34 additions & 1 deletion apps/mystories/tasks/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from firebase_admin import messaging

from apps.mystories.models import Notification
from apps.users.models import ActiveSessions
from apps.users.models import ActiveSessions, User

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -39,3 +39,36 @@ def send_notification_task(notification_id):
logger.info(f"Notification sent to user {notification.user.id}")
except Notification.DoesNotExist:
logger.error(f"Notification with id {notification_id} does not exist")


@shared_task
def send_notification_to_all_task(notification_id):
try:
time.sleep(1)
notification = Notification.objects.get(id=notification_id)
users = User.objects.all()
for user in users:
fcm_tokens = ActiveSessions.objects.filter(
user=user, is_active=True, fcm_token__isnull=False
).values_list("fcm_token", flat=True)
for fcm_token in fcm_tokens:
if fcm_token:
message = messaging.Message(
notification=messaging.Notification(
title=notification.title,
body=notification.message,
image=(
notification.banner.url
if notification.banner
and hasattr(notification.banner, "url")
else None
),
),
token=fcm_token,
)
messaging.send(message)
notification.is_send = True
notification.save()
logger.info("Notification sent to all users")
except Notification.DoesNotExist:
logger.error(f"Notification with id {notification_id} does not exist")
5 changes: 4 additions & 1 deletion apps/mystories/views/posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from drf_spectacular.utils import extend_schema
# from silk.profiling.profiler import silk_profile

from apps.mystories.models import Post
from apps.mystories.serializers.posts import (
PostListSerializer,
Expand Down Expand Up @@ -35,6 +37,7 @@ def get_permissions(self):
def get_queryset(self):
return Post.objects.select_related("author", "theme").prefetch_related("tags")

# @silk_profile()
def get(self, request):
queryset = self.get_queryset()
paginator = self.pagination_class()
Expand Down Expand Up @@ -73,7 +76,7 @@ def get_post(self, pk, user=None):
return get_object_or_404(queryset, pk=pk, author=user)
return get_object_or_404(queryset, pk=pk)

@extend_schema(operation_id="post_detail")
# @silk_profile()
def get(self, request, pk=None):
post = self.get_post(pk)
post.increment_views()
Expand Down
8 changes: 0 additions & 8 deletions apps/shared/middlewares/sessions.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from django.http import JsonResponse
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin
from rest_framework import status
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.authentication import JWTAuthentication

from apps.users.models import ActiveSessions
from apps.users.services import RegisterService


class CheckActiveSessionMiddleware(MiddlewareMixin):
Expand All @@ -26,12 +24,6 @@ def process_request(request):
user=user, access_token=token
).first()
if session:
session.last_activity = timezone.now()
session.ip_address = RegisterService.get_client_ip(request)
session.location = RegisterService.get_location(session.ip_address)
session.user_agent = request.META.get(
"HTTP_USER_AGENT", "Unknown User Agent"
)
if fcm_token:
session.fcm_token = fcm_token
session.save()
Expand Down
44 changes: 40 additions & 4 deletions apps/users/admin/user.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from unfold.decorators import display
from unfold.forms import AdminPasswordChangeForm, UserCreationForm, UserChangeForm
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from apps.users.models import User
from apps.users.models import User, RoleChoices


@admin.register(User)
class UserAdmin(BaseUserAdmin, ModelAdmin):
change_password_form = AdminPasswordChangeForm
add_form = UserCreationForm
form = UserChangeForm
list_display = ("id", "username", "role", "is_active", "email")
list_display = (
"avatars",
"username",
"show_role_customized_color",
"is_active",
"created_at",
)
search_fields = ("email", "username")
list_filter = ("role", "is_active")
list_editable = ("is_active",)
list_display_links = ("id", "username")
list_display_links = ("avatars", "username")
autocomplete_fields = ("country",)
list_per_page = 50
fieldsets = (
Expand Down Expand Up @@ -57,3 +66,30 @@ class UserAdmin(BaseUserAdmin, ModelAdmin):
},
),
)

@display(
description=_("Role"),
ordering="role",
label={
RoleChoices.ADMIN: "success", # green
RoleChoices.MODERATOR: "info", # orange
RoleChoices.USER: "info", # red
},
)
def show_role_customized_color(self, obj):
return obj.role, obj.get_role_display()

@display(header=True, description=_("Avatars"))
def avatars(self, obj):
return [
f"{obj.first_name} {obj.last_name}",
f"ID:{obj.id} - {obj.email}",
"AB",
{
"path": obj.avatar.url if obj.avatar else static("images/avatar.webp"),
"squared": False,
"borderless": True,
"width": 50,
"height": 50,
},
]
Loading
Loading