[lifeevents] Add life events to scrobbles

This commit is contained in:
2024-05-23 00:14:06 -04:00
parent 8ed0bd3d21
commit e15d253d58
12 changed files with 218 additions and 0 deletions

View File

@ -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,
]

View File

@ -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,
},
),
]

View File

@ -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")

View File

@ -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/<slug:slug>/",
views.EventDetailView.as_view(),
name="event_detail",
),
]

View File

@ -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"

View File

@ -24,6 +24,7 @@ class ScrobbleInline(admin.TabularInline):
"board_game", "board_game",
"geo_location", "geo_location",
"web_page", "web_page",
"life_event",
"user", "user",
) )
exclude = ( exclude = (
@ -118,6 +119,7 @@ class ScrobbleAdmin(admin.ModelAdmin):
"board_game", "board_game",
"geo_location", "geo_location",
"web_page", "web_page",
"life_event",
) )
list_filter = ( list_filter = (
"is_paused", "is_paused",

View File

@ -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,
),
),
]

View File

@ -39,6 +39,7 @@ from videogames import retroarch
from videogames.models import VideoGame from videogames.models import VideoGame
from videos.models import Series, Video from videos.models import Series, Video
from webpages.models import WebPage from webpages.models import WebPage
from lifeevents.models import LifeEvent
from vrobbler.apps.scrobbles.constants import MEDIA_END_PADDING_SECONDS from vrobbler.apps.scrobbles.constants import MEDIA_END_PADDING_SECONDS
@ -481,6 +482,7 @@ class Scrobble(TimeStampedModel):
BOARD_GAME = "BoardGame", "Board game" BOARD_GAME = "BoardGame", "Board game"
GEO_LOCATION = "GeoLocation", "GeoLocation" GEO_LOCATION = "GeoLocation", "GeoLocation"
WEBPAGE = "WebPage", "Web Page" WEBPAGE = "WebPage", "Web Page"
LIFE_EVENT = "LifeEvent", "Life event"
uuid = models.UUIDField(editable=False, **BNULL) uuid = models.UUIDField(editable=False, **BNULL)
video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **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 GeoLocation, on_delete=models.DO_NOTHING, **BNULL
) )
web_page = models.ForeignKey(WebPage, 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( media_type = models.CharField(
max_length=14, choices=MediaType.choices, default=MediaType.VIDEO max_length=14, choices=MediaType.choices, default=MediaType.VIDEO
) )
@ -809,6 +814,8 @@ class Scrobble(TimeStampedModel):
media_obj = self.geo_location media_obj = self.geo_location
if self.web_page: if self.web_page:
media_obj = self.web_page media_obj = self.web_page
if self.life_event:
media_obj = self.life_event
return media_obj return media_obj
def __str__(self): def __str__(self):

View File

@ -110,6 +110,7 @@ INSTALLED_APPS = [
"videogames", "videogames",
"locations", "locations",
"webpages", "webpages",
"lifeevents",
"mathfilters", "mathfilters",
"rest_framework", "rest_framework",
"allauth", "allauth",

View File

@ -124,6 +124,7 @@ INSTALLED_APPS = [
"videogames", "videogames",
"locations", "locations",
"webpages", "webpages",
"lifeevents",
"mathfilters", "mathfilters",
"rest_framework", "rest_framework",
"allauth", "allauth",

View File

@ -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.videogames import urls as videogame_urls
from vrobbler.apps.boardgames import urls as boardgame_urls from vrobbler.apps.boardgames import urls as boardgame_urls
from vrobbler.apps.locations import urls as locations_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.webpages import urls as webpages_urls
from vrobbler.apps.music.api.views import ( from vrobbler.apps.music.api.views import (
AlbumViewSet, AlbumViewSet,
@ -71,6 +72,7 @@ urlpatterns = [
path("", include(locations_urls, namespace="locations")), path("", include(locations_urls, namespace="locations")),
path("", include(webpages_urls, namespace="webpages")), path("", include(webpages_urls, namespace="webpages")),
path("", include(podcast_urls, namespace="podcasts")), path("", include(podcast_urls, namespace="podcasts")),
path("", include(lifeevents_urls, namespace="life-events")),
path("", include(scrobble_urls, namespace="scrobbles")), path("", include(scrobble_urls, namespace="scrobbles")),
path( path(
"", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home" "", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home"