Skip to content

Commit

Permalink
Merge pull request #41 from OZ-Coding-School/dev
Browse files Browse the repository at this point in the history
Dev -> main 관리자 페이지 알림, popular 태그
  • Loading branch information
Gomnonix authored Sep 9, 2024
2 parents 8cbf659 + 750697e commit d3a9de8
Show file tree
Hide file tree
Showing 36 changed files with 693 additions and 18 deletions.
35 changes: 33 additions & 2 deletions customk/classes/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
import uuid
from datetime import timedelta

from django.db.models import Avg
from django.core.cache import cache
from django.db.models import Avg, Count
from django.utils import timezone
from rest_framework import serializers

from common.services.ncp_api_conf import ObjectStorage
from config.logger import logger
from payments.models import Payment

from .models import Category, Class, ClassDate, ClassImages, Genre
from .models import Category, Class, ClassDate, ClassImages


def upload_image_to_object_storage(base64_image: str) -> str:
Expand Down Expand Up @@ -80,6 +82,7 @@ class ClassSerializer(serializers.ModelSerializer):
)
average_rating = serializers.FloatField(read_only=True)
created_at = serializers.DateTimeField(read_only=True)
is_popular = serializers.SerializerMethodField()

class Meta:
model = Class
Expand Down Expand Up @@ -114,6 +117,34 @@ def get_is_best(self, obj):

return average_rating >= 3.5

def get_is_popular(self, obj):
popular_classes = cache.get("popular_classes", None)

if popular_classes is None:
one_month_ago = timezone.now() - timedelta(days=30)

classes_with_payments = (
Payment.objects.filter(created_at__gte=one_month_ago)
.values("class_id")
.annotate(payment_count=Count("id"))
.order_by("-payment_count")
)

total_classes = len(classes_with_payments)
top_20_percent_count = max(1, int(total_classes * 0.2))

popular_class_ids = [
entry["class_id"]
for entry in classes_with_payments[:top_20_percent_count]
]

cache.set("popular_classes", popular_class_ids, timeout=60 * 60 * 24 * 14)
popular_classes = popular_class_ids

is_popular = obj.id in popular_classes
print(f"Class ID: {obj.id}, Is Popular: {is_popular}")
return is_popular

def create(self, validated_data):
dates_data = validated_data.pop("dates", [])
images_data64 = validated_data.pop("images", [])
Expand Down
6 changes: 5 additions & 1 deletion customk/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"corsheaders",
"favorites",
"payments",
"notifications",
]


Expand Down Expand Up @@ -79,7 +80,7 @@
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"DIRS": [BASE_DIR / "questions/templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
Expand Down Expand Up @@ -191,6 +192,9 @@

STATIC_URL = "/static/"
STATIC_ROOT = "/vol/web/static"
STATICFILES_DIRS = [
BASE_DIR / "questions/static",
]

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

Expand Down
1 change: 1 addition & 0 deletions customk/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@
re_path(r"^v1/reviews/?", include("reviews.urls")),
re_path(r"^v1/favorites/?", include("favorites.urls")),
re_path(r"^v1/reactions/?", include("reactions.urls")),
re_path(r"^v1/notifications/?", include("notifications.urls")),
re_path(r"^v1/payments/?", include("payments.urls")),
]
File renamed without changes.
29 changes: 29 additions & 0 deletions customk/notifications/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.contrib import admin

from .models import PaymentNotification, QuestionNotification


@admin.register(QuestionNotification)
class QuestionNotificationAdmin(admin.ModelAdmin):
list_display = ("message", "class_field", "created_at", "is_read")
list_filter = ("is_read", "question__class_id")
search_fields = ("message", "question__class_id__title")

def class_field(self, obj):
return obj.question.class_id.title

class_field.admin_order_field = "question__class_id__title" # type: ignore
class_field.short_description = "Class Title" # type: ignore


@admin.register(PaymentNotification)
class PaymentNotificationAdmin(admin.ModelAdmin):
list_display = ("message", "payment_method", "created_at", "is_read")
list_filter = ("is_read", "payment__payment_method")
search_fields = ("message", "payment__order_id")

def payment_method(self, obj):
return obj.payment.payment_method

payment_method.admin_order_field = "payment__payment_method" # type: ignore
payment_method.short_description = "Payment Method" # type: ignore
6 changes: 6 additions & 0 deletions customk/notifications/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class NotificationsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "notifications"
44 changes: 44 additions & 0 deletions customk/notifications/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 5.1 on 2024-09-06 05:59

import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = [
("questions", "0006_remove_question_answer_user_id_alter_question_answer"),
]

operations = [
migrations.CreateModel(
name="Notification",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(default=django.utils.timezone.now)),
("updated_at", models.DateTimeField(auto_now=True)),
("message", models.CharField(max_length=255)),
("is_read", models.BooleanField(default=False)),
(
"question",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="questions.question",
),
),
],
options={
"abstract": False,
},
),
]
11 changes: 11 additions & 0 deletions customk/notifications/migrations/0002_auto_20240906_1518.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Generated by Django 5.1 on 2024-09-06 06:18

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("notifications", "0001_initial"),
]

operations = []
11 changes: 11 additions & 0 deletions customk/notifications/migrations/0003_auto_20240906_1550.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Generated by Django 5.1 on 2024-09-06 06:50

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("notifications", "0002_auto_20240906_1518"),
]

operations = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 5.1 on 2024-09-08 12:36

import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("notifications", "0003_auto_20240906_1550"),
("payments", "0002_payment_refunded_amount"),
("questions", "0006_remove_question_answer_user_id_alter_question_answer"),
]

operations = [
migrations.RenameModel(
old_name="Notification",
new_name="PaymentNotification",
),
migrations.RemoveField(
model_name="paymentnotification",
name="question",
),
migrations.AddField(
model_name="paymentnotification",
name="payment",
field=models.ForeignKey(
default="",
on_delete=django.db.models.deletion.CASCADE,
to="payments.payment",
),
preserve_default=False,
),
migrations.CreateModel(
name="QuestionNotification",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(default=django.utils.timezone.now)),
("updated_at", models.DateTimeField(auto_now=True)),
("message", models.CharField(max_length=255)),
("is_read", models.BooleanField(default=False)),
(
"question",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="questions.question",
),
),
],
options={
"abstract": False,
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2024-09-08 12:41

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("notifications", "0004_rename_notification_paymentnotification_and_more"),
("payments", "0002_payment_refunded_amount"),
]

operations = [
migrations.AlterField(
model_name="paymentnotification",
name="payment",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="payments.payment",
),
),
]
Empty file.
27 changes: 27 additions & 0 deletions customk/notifications/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.db import models

from common.models import CommonModel
from payments.models import Payment
from questions.models import Question


class QuestionNotification(CommonModel):
message = models.CharField(max_length=255)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
is_read = models.BooleanField(default=False)

def __str__(self):
return self.message


class PaymentNotification(CommonModel):
message = models.CharField(max_length=255)
payment = models.ForeignKey(
Payment,
on_delete=models.CASCADE,
null=True,
)
is_read = models.BooleanField(default=False)

def __str__(self):
return self.message
File renamed without changes.
16 changes: 16 additions & 0 deletions customk/notifications/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.urls import path

from . import views

urlpatterns = [
path(
"unread_question_notifications_count/",
views.unread_question_notifications_count,
name="unread_question_notifications_count",
),
path(
"unread_payment_notifications_count/",
views.unread_payment_notifications_count,
name="unread_payment_notifications_count",
),
]
13 changes: 13 additions & 0 deletions customk/notifications/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.http import JsonResponse

from .models import PaymentNotification, QuestionNotification


def unread_question_notifications_count(request):
count = QuestionNotification.objects.filter(is_read=False).count()
return JsonResponse({"count": count})


def unread_payment_notifications_count(request):
count = PaymentNotification.objects.filter(is_read=False).count()
return JsonResponse({"count": count})
24 changes: 23 additions & 1 deletion customk/payments/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
from django.contrib import admin

from payments.models import ReferralCode
from payments.models import Payment, ReferralCode


@admin.register(ReferralCode)
class ReferralCodeAdmin(admin.ModelAdmin): # type: ignore
pass


@admin.register(Payment)
class PaymentAdmin(admin.ModelAdmin):
list_display = (
"order_id",
"status",
"amount",
"refunded_amount",
"currency",
"payment_method",
"payer_email",
"user_id",
"class_id",
"class_date_id",
"quantity",
"referral_code",
"transaction_id",
)
search_fields = ("order_id", "payment_method", "payer_email", "transaction_id")
list_filter = ("status", "currency", "payment_method")
readonly_fields = [field.name for field in Payment._meta.fields]
3 changes: 3 additions & 0 deletions customk/payments/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class PaymentsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "payments"

def ready(self):
import payments.signals
Loading

0 comments on commit d3a9de8

Please sign in to comment.