From e15d253d5836e124d0b5189118e6063fc3a808a3 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Thu, 23 May 2024 00:14:06 -0400 Subject: [PATCH] [lifeevents] Add life events to scrobbles --- vrobbler/apps/lifeevents/admin.py | 16 ++++ .../lifeevents/migrations/0001_initial.py | 79 +++++++++++++++++++ .../apps/lifeevents/migrations/__init__.py | 0 vrobbler/apps/lifeevents/models.py | 39 +++++++++ vrobbler/apps/lifeevents/urls.py | 14 ++++ vrobbler/apps/lifeevents/views.py | 12 +++ vrobbler/apps/scrobbles/admin.py | 2 + ...le_life_event_alter_scrobble_media_type.py | 45 +++++++++++ vrobbler/apps/scrobbles/models.py | 7 ++ vrobbler/settings-testing.py | 1 + vrobbler/settings.py | 1 + vrobbler/urls.py | 2 + 12 files changed, 218 insertions(+) create mode 100644 vrobbler/apps/lifeevents/admin.py create mode 100644 vrobbler/apps/lifeevents/migrations/0001_initial.py create mode 100644 vrobbler/apps/lifeevents/migrations/__init__.py create mode 100644 vrobbler/apps/lifeevents/models.py create mode 100644 vrobbler/apps/lifeevents/urls.py create mode 100644 vrobbler/apps/lifeevents/views.py create mode 100644 vrobbler/apps/scrobbles/migrations/0056_scrobble_life_event_alter_scrobble_media_type.py diff --git a/vrobbler/apps/lifeevents/admin.py b/vrobbler/apps/lifeevents/admin.py new file mode 100644 index 0000000..4a22f53 --- /dev/null +++ b/vrobbler/apps/lifeevents/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin + +from lifeevents.models import LifeEvent + +from scrobbles.admin import ScrobbleInline + + +@admin.register(LifeEvent) +class EventAdmin(admin.ModelAdmin): + date_hierarchy = "created" + list_display = ("title",) + search_fields = ("title",) + ordering = ("-created",) + inlines = [ + ScrobbleInline, + ] diff --git a/vrobbler/apps/lifeevents/migrations/0001_initial.py b/vrobbler/apps/lifeevents/migrations/0001_initial.py new file mode 100644 index 0000000..239c288 --- /dev/null +++ b/vrobbler/apps/lifeevents/migrations/0001_initial.py @@ -0,0 +1,79 @@ +# Generated by Django 4.2.11 on 2024-05-07 13:37 + +from django.db import migrations, models +import django_extensions.db.fields +import taggit.managers +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("scrobbles", "0055_rename_scrobble_log_scrobble_log"), + ] + + operations = [ + migrations.CreateModel( + name="LifeEvent", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created", + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name="created" + ), + ), + ( + "modified", + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name="modified" + ), + ), + ( + "uuid", + models.UUIDField( + blank=True, + default=uuid.uuid4, + editable=False, + null=True, + ), + ), + ( + "title", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "run_time_seconds", + models.IntegerField(blank=True, null=True), + ), + ( + "run_time_ticks", + models.PositiveBigIntegerField(blank=True, null=True), + ), + ("description", models.TextField(blank=True, null=True)), + ( + "genre", + taggit.managers.TaggableManager( + blank=True, + help_text="A comma-separated list of tags.", + through="scrobbles.ObjectWithGenres", + to="scrobbles.Genre", + verbose_name="Tags", + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/vrobbler/apps/lifeevents/migrations/__init__.py b/vrobbler/apps/lifeevents/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vrobbler/apps/lifeevents/models.py b/vrobbler/apps/lifeevents/models.py new file mode 100644 index 0000000..f3e9f93 --- /dev/null +++ b/vrobbler/apps/lifeevents/models.py @@ -0,0 +1,39 @@ +from django.apps import apps +from django.db import models +from django.urls import reverse +import pendulum +from lifeevents.dataclasses import LifeEventMetadata +from scrobbles.mixins import ScrobblableMixin + +BNULL = {"blank": True, "null": True} + + +class LifeEvent(ScrobblableMixin): + COMPLETION_PERCENT = 100 + + description = models.TextField(**BNULL) + + def get_absolute_url(self): + return reverse( + "life-events:life-event_detail", kwargs={"slug": self.uuid} + ) + + @property + def metadata_class(self): + return LifeEventMetadata + + @classmethod + def find_or_create(cls, title: str) -> "LifeEvent": + return cls.objects.filter(title=title).first() + + def scrobble_for_user(self, user_id): + Scrobble = apps.get_model("scrobbles", "Scrobble") + return Scrobble.objects.create( + user_id=user_id, life_event=self, timestamp=pendulum.now() + ) + + def scrobbles(self, user_id): + Scrobble = apps.get_model("scrobbles", "Scrobble") + return Scrobble.objects.filter( + user_id=user_id, life_event=self + ).order_by("-timestamp") diff --git a/vrobbler/apps/lifeevents/urls.py b/vrobbler/apps/lifeevents/urls.py new file mode 100644 index 0000000..b922f60 --- /dev/null +++ b/vrobbler/apps/lifeevents/urls.py @@ -0,0 +1,14 @@ +from django.urls import path +from lifeevents import views + +app_name = "events" + + +urlpatterns = [ + path("events/", views.EventListView.as_view(), name="event_list"), + path( + "event//", + views.EventDetailView.as_view(), + name="event_detail", + ), +] diff --git a/vrobbler/apps/lifeevents/views.py b/vrobbler/apps/lifeevents/views.py new file mode 100644 index 0000000..1b28622 --- /dev/null +++ b/vrobbler/apps/lifeevents/views.py @@ -0,0 +1,12 @@ +from django.views import generic +from lifeevents.models import LifeEvent + + +class EventListView(generic.ListView): + model = LifeEvent + paginate_by = 20 + + +class EventDetailView(generic.DetailView): + model = LifeEvent + slug_field = "uuid" diff --git a/vrobbler/apps/scrobbles/admin.py b/vrobbler/apps/scrobbles/admin.py index 387bfd9..fca9e08 100644 --- a/vrobbler/apps/scrobbles/admin.py +++ b/vrobbler/apps/scrobbles/admin.py @@ -24,6 +24,7 @@ class ScrobbleInline(admin.TabularInline): "board_game", "geo_location", "web_page", + "life_event", "user", ) exclude = ( @@ -118,6 +119,7 @@ class ScrobbleAdmin(admin.ModelAdmin): "board_game", "geo_location", "web_page", + "life_event", ) list_filter = ( "is_paused", diff --git a/vrobbler/apps/scrobbles/migrations/0056_scrobble_life_event_alter_scrobble_media_type.py b/vrobbler/apps/scrobbles/migrations/0056_scrobble_life_event_alter_scrobble_media_type.py new file mode 100644 index 0000000..656c024 --- /dev/null +++ b/vrobbler/apps/scrobbles/migrations/0056_scrobble_life_event_alter_scrobble_media_type.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.11 on 2024-05-07 13:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("lifeevents", "0001_initial"), + ("scrobbles", "0055_rename_scrobble_log_scrobble_log"), + ] + + operations = [ + migrations.AddField( + model_name="scrobble", + name="life_event", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + to="lifeevents.lifeevent", + ), + ), + migrations.AlterField( + model_name="scrobble", + name="media_type", + field=models.CharField( + choices=[ + ("Video", "Video"), + ("Track", "Track"), + ("PodcastEpisode", "Podcast episode"), + ("SportEvent", "Sport event"), + ("Book", "Book"), + ("VideoGame", "Video game"), + ("BoardGame", "Board game"), + ("GeoLocation", "GeoLocation"), + ("WebPage", "Web Page"), + ("LifeEvent", "Life event"), + ], + default="Video", + max_length=14, + ), + ), + ] diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index 76ca74f..7a9c69d 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -39,6 +39,7 @@ from videogames import retroarch from videogames.models import VideoGame from videos.models import Series, Video from webpages.models import WebPage +from lifeevents.models import LifeEvent from vrobbler.apps.scrobbles.constants import MEDIA_END_PADDING_SECONDS @@ -481,6 +482,7 @@ class Scrobble(TimeStampedModel): BOARD_GAME = "BoardGame", "Board game" GEO_LOCATION = "GeoLocation", "GeoLocation" WEBPAGE = "WebPage", "Web Page" + LIFE_EVENT = "LifeEvent", "Life event" uuid = models.UUIDField(editable=False, **BNULL) video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **BNULL) @@ -502,6 +504,9 @@ class Scrobble(TimeStampedModel): GeoLocation, on_delete=models.DO_NOTHING, **BNULL ) web_page = models.ForeignKey(WebPage, on_delete=models.DO_NOTHING, **BNULL) + life_event = models.ForeignKey( + LifeEvent, on_delete=models.DO_NOTHING, **BNULL + ) media_type = models.CharField( max_length=14, choices=MediaType.choices, default=MediaType.VIDEO ) @@ -809,6 +814,8 @@ class Scrobble(TimeStampedModel): media_obj = self.geo_location if self.web_page: media_obj = self.web_page + if self.life_event: + media_obj = self.life_event return media_obj def __str__(self): diff --git a/vrobbler/settings-testing.py b/vrobbler/settings-testing.py index abb4c89..e52663f 100644 --- a/vrobbler/settings-testing.py +++ b/vrobbler/settings-testing.py @@ -110,6 +110,7 @@ INSTALLED_APPS = [ "videogames", "locations", "webpages", + "lifeevents", "mathfilters", "rest_framework", "allauth", diff --git a/vrobbler/settings.py b/vrobbler/settings.py index d7b178b..8bdce1b 100644 --- a/vrobbler/settings.py +++ b/vrobbler/settings.py @@ -124,6 +124,7 @@ INSTALLED_APPS = [ "videogames", "locations", "webpages", + "lifeevents", "mathfilters", "rest_framework", "allauth", diff --git a/vrobbler/urls.py b/vrobbler/urls.py index a6218a0..7efa1af 100644 --- a/vrobbler/urls.py +++ b/vrobbler/urls.py @@ -11,6 +11,7 @@ from vrobbler.apps.podcasts import urls as podcast_urls from vrobbler.apps.videogames import urls as videogame_urls from vrobbler.apps.boardgames import urls as boardgame_urls from vrobbler.apps.locations import urls as locations_urls +from vrobbler.apps.lifeevents import urls as lifeevents_urls from vrobbler.apps.webpages import urls as webpages_urls from vrobbler.apps.music.api.views import ( AlbumViewSet, @@ -71,6 +72,7 @@ urlpatterns = [ path("", include(locations_urls, namespace="locations")), path("", include(webpages_urls, namespace="webpages")), path("", include(podcast_urls, namespace="podcasts")), + path("", include(lifeevents_urls, namespace="life-events")), path("", include(scrobble_urls, namespace="scrobbles")), path( "", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home"