Skip to content
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
18 changes: 18 additions & 0 deletions promo_code/business/migrations/0003_promo_like_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2 on 2025-05-13 19:41

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("business", "0002_promo_used_count_alter_company_token_version_and_more"),
]

operations = [
migrations.AddField(
model_name="promo",
name="like_count",
field=models.PositiveIntegerField(default=0, editable=False),
),
]
8 changes: 8 additions & 0 deletions promo_code/business/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class Promo(django.db.models.Model):
default=0,
editable=False,
)
like_count = django.db.models.PositiveIntegerField(
default=0,
editable=False,
)
active_from = django.db.models.DateField(null=True, blank=True)
active_until = django.db.models.DateField(null=True, blank=True)
mode = django.db.models.CharField(
Expand Down Expand Up @@ -99,6 +103,10 @@ def is_active(self) -> bool:

return True

@property
def get_like_count(self) -> int:
return self.like_count

@property
def get_used_codes_count(self) -> int:
if self.mode == business.constants.PROMO_MODE_UNIQUE:
Expand Down
18 changes: 8 additions & 10 deletions promo_code/business/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,10 @@ class PromoReadOnlySerializer(rest_framework.serializers.ModelSerializer):
target = TargetSerializer()

promo_unique = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.IntegerField(
source='get_like_count',
read_only=True,
)
used_count = rest_framework.serializers.IntegerField(
source='get_used_codes_count',
read_only=True,
Expand Down Expand Up @@ -439,10 +442,6 @@ class Meta:
def get_promo_unique(self, obj):
return obj.get_available_unique_codes

def get_like_count(self, obj):
# TODO
return 0

def to_representation(self, instance):
data = super().to_representation(instance)
if instance.mode == business.constants.PROMO_MODE_COMMON:
Expand Down Expand Up @@ -476,7 +475,10 @@ class PromoDetailSerializer(rest_framework.serializers.ModelSerializer):
source='company.name',
read_only=True,
)
like_count = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.IntegerField(
source='get_like_count',
read_only=True,
)
used_count = rest_framework.serializers.IntegerField(
source='get_used_codes_count',
read_only=True,
Expand Down Expand Up @@ -526,7 +528,3 @@ def validate(self, data):
instance=self.instance,
)
return validator.validate()

def get_like_count(self, obj):
# TODO
return 0
56 changes: 56 additions & 0 deletions promo_code/user/migrations/0002_promolike.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 5.2 on 2025-05-12 17:44

import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("business", "0002_promo_used_count_alter_company_token_version_and_more"),
("user", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="PromoLike",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
verbose_name="UUID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"promo",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="likes",
to="business.promo",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="promo_likes",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"constraints": [
models.UniqueConstraint(
fields=("user", "promo"), name="unique_like"
)
],
},
),
]
32 changes: 32 additions & 0 deletions promo_code/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import django.db.models
import django.utils.timezone

import business.models
import user.constants


Expand Down Expand Up @@ -82,3 +83,34 @@ def save(self, *args, **kwargs):
self.last_login = django.utils.timezone.now()

super().save(*args, **kwargs)


class PromoLike(django.db.models.Model):
id = django.db.models.UUIDField(
'UUID',
primary_key=True,
default=uuid.uuid4,
editable=False,
)
user = django.db.models.ForeignKey(
User,
on_delete=django.db.models.CASCADE,
related_name='promo_likes',
)
promo = django.db.models.ForeignKey(
business.models.Promo,
on_delete=django.db.models.CASCADE,
related_name='likes',
)
created_at = django.db.models.DateTimeField(auto_now_add=True)

class Meta:
constraints = [
django.db.models.UniqueConstraint(
fields=['user', 'promo'],
name='unique_like',
),
]

def __str__(self):
return f'{self.user} likes {self.promo}'
33 changes: 20 additions & 13 deletions promo_code/user/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,10 @@ class PromoFeedSerializer(rest_framework.serializers.ModelSerializer):
active = rest_framework.serializers.BooleanField(source='is_active')
is_activated_by_user = rest_framework.serializers.SerializerMethodField()
is_liked_by_user = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.IntegerField(
source='get_like_count',
read_only=True,
)
comment_count = rest_framework.serializers.SerializerMethodField()

class Meta:
Expand All @@ -366,10 +369,6 @@ def get_is_activated_by_user(self, obj) -> bool:
# TODO:
return False

def get_like_count(self, obj) -> int:
# TODO:
return 0

def get_is_liked_by_user(self, obj) -> bool:
# TODO:
return False
Expand Down Expand Up @@ -403,7 +402,10 @@ class UserPromoDetailSerializer(rest_framework.serializers.ModelSerializer):
read_only=True,
)
is_activated_by_user = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.IntegerField(
source='get_like_count',
read_only=True,
)
is_liked_by_user = rest_framework.serializers.SerializerMethodField()
comment_count = rest_framework.serializers.SerializerMethodField()

Expand All @@ -423,15 +425,20 @@ class Meta:
)
read_only_fields = fields

def get_is_activated_by_user(self, obj) -> bool:
# TODO:
def get_is_liked_by_user(self, obj: business.models.Promo) -> bool:
request = self.context.get('request')
if (
request
and hasattr(request, 'user')
and request.user.is_authenticated
):
return user.models.PromoLike.objects.filter(
promo=obj,
user=request.user,
).exists()
return False

def get_like_count(self, obj) -> int:
# TODO:
return 0

def get_is_liked_by_user(self, obj) -> bool:
def get_is_activated_by_user(self, obj) -> bool:
# TODO:
return False

Expand Down
5 changes: 5 additions & 0 deletions promo_code/user/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@
user.views.UserPromoDetailView.as_view(),
name='user-promo-detail',
),
django.urls.path(
'promo/<uuid:id>/like',
user.views.UserPromoLikeView.as_view(),
name='user-promo-like',
),
]
52 changes: 52 additions & 0 deletions promo_code/user/views.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import django.db.models
import django.shortcuts
import django.utils.timezone
import rest_framework.generics
import rest_framework.permissions
import rest_framework.response
import rest_framework.status
import rest_framework.views
import rest_framework_simplejwt.tokens
import rest_framework_simplejwt.views

import business.constants
import business.models
import user.models
import user.pagination
import user.serializers

Expand Down Expand Up @@ -224,3 +227,52 @@ def list(self, request, *args, **kwargs):
self.validated_query_params = query_serializer.validated_data

return super().list(request, *args, **kwargs)


class UserPromoLikeView(rest_framework.views.APIView):
permission_classes = [rest_framework.permissions.IsAuthenticated]

def get_promo_object(self, promo_id):
return django.shortcuts.get_object_or_404(
business.models.Promo,
id=promo_id,
)

def post(self, request, id):
"""Add a like to the promo code."""
promo = self.get_promo_object(id)

created = user.models.PromoLike.objects.get_or_create(
user=request.user,
promo=promo,
)

if created:
promo.like_count = django.db.models.F('like_count') + 1
promo.save(update_fields=['like_count'])

return rest_framework.response.Response(
{'status': 'ok'},
status=rest_framework.status.HTTP_200_OK,
)

def delete(self, request, id):
"""Remove a like from the promo code."""
promo = self.get_promo_object(id)

# Idempotency: if the like doesn't exist,
# do nothing and still return 200 OK.
like_instance = user.models.PromoLike.objects.filter(
user=request.user,
promo=promo,
).first()

if like_instance:
like_instance.delete()
promo.like_count = django.db.models.F('like_count') - 1
promo.save(update_fields=['like_count'])

return rest_framework.response.Response(
{'status': 'ok'},
status=rest_framework.status.HTTP_200_OK,
)