Skip to content

Commit

Permalink
playlists: add played level count
Browse files Browse the repository at this point in the history
  • Loading branch information
rr- committed Aug 6, 2023
1 parent 076bfa8 commit b98af14
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 6 deletions.
3 changes: 3 additions & 0 deletions backend/trcustoms/playlists/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
class PlaylistsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "trcustoms.playlists"

def ready(self):
import trcustoms.playlists.signals # noqa: F401
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 4.2.3 on 2023-08-06 13:36

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


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("levels", "0014_auto_20230509_1033"),
("playlists", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="playlistitem",
name="level",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="playlist_items",
to="levels.level",
),
),
migrations.AlterField(
model_name="playlistitem",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="playlist_items",
to=settings.AUTH_USER_MODEL,
),
),
]
20 changes: 18 additions & 2 deletions backend/trcustoms/playlists/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,26 @@
from trcustoms.users.models import User


class PlaylistItemQuerySet(models.QuerySet):
def played(self) -> models.QuerySet:
return self.filter(
status__in=[
PlaylistStatus.FINISHED,
PlaylistStatus.PLAYING,
PlaylistStatus.DROPPED,
PlaylistStatus.ON_HOLD,
]
)


class PlaylistItem(DatesInfo):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="+")
objects = PlaylistItemQuerySet.as_manager()

user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="playlist_items"
)
level = models.ForeignKey(
Level, on_delete=models.CASCADE, related_name="+"
Level, on_delete=models.CASCADE, related_name="playlist_items"
)
status = models.CharField(
choices=PlaylistStatus.choices,
Expand Down
19 changes: 19 additions & 0 deletions backend/trcustoms/playlists/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.db.models.signals import post_delete, post_save, pre_delete
from django.dispatch import receiver

from trcustoms.playlists.models import PlaylistItem


@receiver(post_save, sender=PlaylistItem)
def update_level_player_on_playlist_item_change(sender, instance, **kwargs):
instance.user.update_played_level_count()


@receiver(pre_delete, sender=PlaylistItem)
def remember_playlist_item_user(sender, instance, using, **kwargs):
instance._old_user = instance.user


@receiver(post_delete, sender=PlaylistItem)
def update_level_player_on_playlist_item_delete(sender, instance, **kwargs):
instance._old_user.update_played_level_count()
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,11 @@ def test_playlist_item_creation_success(
assert playlist_item.user.pk == auth_api_client.user.pk
assert playlist_item.level.pk == level.pk
assert playlist_item.status == PlaylistStatus.ON_HOLD


@pytest.mark.django_db
def test_playlist_item_creation_updates_played_level_count() -> None:
user = UserFactory()
PlaylistItemFactory(user=user, status=PlaylistStatus.FINISHED)
user.refresh_from_db()
assert user.played_level_count == 1
25 changes: 21 additions & 4 deletions backend/trcustoms/playlists/tests/test_playlist_items_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from rest_framework import status
from rest_framework.test import APIClient

from trcustoms.playlists.consts import PlaylistStatus
from trcustoms.playlists.models import PlaylistItem
from trcustoms.playlists.tests.factories import PlaylistItemFactory
from trcustoms.users.tests.factories import UserFactory


@pytest.mark.django_db
def test_walkthrough_deletion_requires_login(
def test_playlist_item_deletion_requires_login(
api_client: APIClient,
) -> None:
playlist_item = PlaylistItemFactory()
Expand All @@ -22,7 +24,7 @@ def test_walkthrough_deletion_requires_login(


@pytest.mark.django_db
def test_walkthrough_deletion_rejects_non_owner(
def test_playlist_item_deletion_rejects_non_owner(
auth_api_client: APIClient,
) -> None:
playlist_item = PlaylistItemFactory(user__username="unique user")
Expand All @@ -37,7 +39,7 @@ def test_walkthrough_deletion_rejects_non_owner(


@pytest.mark.django_db
def test_walkthrough_deletion_accepts_owner(
def test_playlist_item_deletion_accepts_owner(
auth_api_client: APIClient,
) -> None:
playlist_item = PlaylistItemFactory(user=auth_api_client.user)
Expand All @@ -49,7 +51,7 @@ def test_walkthrough_deletion_accepts_owner(


@pytest.mark.django_db
def test_walkthrough_deletion_accepts_admin(
def test_playlist_item_deletion_accepts_admin(
staff_api_client: APIClient,
) -> None:
playlist_item = PlaylistItemFactory()
Expand All @@ -58,3 +60,18 @@ def test_walkthrough_deletion_accepts_admin(
)
assert resp.status_code == status.HTTP_204_NO_CONTENT
assert not PlaylistItem.objects.filter(pk=playlist_item.pk).exists()


@pytest.mark.django_db
def test_playlist_item_deletion_updates_played_level_count(
superuser_api_client: APIClient,
) -> None:
user = UserFactory()
playlist_item = PlaylistItemFactory(
user=user, status=PlaylistStatus.FINISHED
)
user.refresh_from_db()
assert user.played_level_count == 1
playlist_item.delete()
user.refresh_from_db()
assert user.played_level_count == 0
17 changes: 17 additions & 0 deletions backend/trcustoms/users/migrations/0014_user_played_level_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.3 on 2023-08-06 13:36

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("users", "0013_alter_user_options"),
]

operations = [
migrations.AddField(
model_name="user",
name="played_level_count",
field=models.PositiveIntegerField(default=0),
),
]
5 changes: 5 additions & 0 deletions backend/trcustoms/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class Meta(AbstractUser.Meta):
Country, null=True, blank=True, on_delete=models.SET_NULL
)

played_level_count = models.PositiveIntegerField(default=0)
authored_level_count = models.PositiveIntegerField(default=0)
reviewed_level_count = models.PositiveIntegerField(default=0)
authored_walkthrough_count = models.PositiveIntegerField(default=0)
Expand All @@ -126,6 +127,10 @@ def update_reviewed_level_count(self) -> None:
).count()
self.save(update_fields=["reviewed_level_count"])

def update_played_level_count(self) -> None:
self.played_level_count = self.playlist_items.played().count()
self.save(update_fields=["played_level_count"])

def update_authored_level_count(self) -> None:
self.authored_level_count = self.authored_levels.filter(
is_approved=True
Expand Down
2 changes: 2 additions & 0 deletions backend/trcustoms/users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class UserListingSerializer(serializers.ModelSerializer):
permissions = serializers.SerializerMethodField(read_only=True)
date_joined = serializers.ReadOnlyField()
last_login = serializers.ReadOnlyField()
played_level_count = serializers.ReadOnlyField()
authored_level_count = serializers.ReadOnlyField()
authored_walkthrough_count = serializers.ReadOnlyField()
reviewed_level_count = serializers.ReadOnlyField()
Expand All @@ -62,6 +63,7 @@ class Meta:
"is_active",
"is_banned",
"is_pending_activation",
"played_level_count",
"authored_level_count",
"authored_walkthrough_count",
"reviewed_level_count",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface UserListing extends UserNested {
is_active: boolean;
is_banned: boolean;
is_pending_activation: boolean;
played_level_count: number;
authored_level_count: number;
reviewed_level_count: number;
authored_walkthrough_count: number;
Expand Down

0 comments on commit b98af14

Please sign in to comment.