[lifeevents] Add life events to scrobbles
This commit is contained in:
16
vrobbler/apps/lifeevents/admin.py
Normal file
16
vrobbler/apps/lifeevents/admin.py
Normal 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,
|
||||||
|
]
|
||||||
79
vrobbler/apps/lifeevents/migrations/0001_initial.py
Normal file
79
vrobbler/apps/lifeevents/migrations/0001_initial.py
Normal 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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
vrobbler/apps/lifeevents/migrations/__init__.py
Normal file
0
vrobbler/apps/lifeevents/migrations/__init__.py
Normal file
39
vrobbler/apps/lifeevents/models.py
Normal file
39
vrobbler/apps/lifeevents/models.py
Normal 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")
|
||||||
14
vrobbler/apps/lifeevents/urls.py
Normal file
14
vrobbler/apps/lifeevents/urls.py
Normal 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",
|
||||||
|
),
|
||||||
|
]
|
||||||
12
vrobbler/apps/lifeevents/views.py
Normal file
12
vrobbler/apps/lifeevents/views.py
Normal 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"
|
||||||
@ -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",
|
||||||
|
|||||||
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -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):
|
||||||
|
|||||||
@ -110,6 +110,7 @@ INSTALLED_APPS = [
|
|||||||
"videogames",
|
"videogames",
|
||||||
"locations",
|
"locations",
|
||||||
"webpages",
|
"webpages",
|
||||||
|
"lifeevents",
|
||||||
"mathfilters",
|
"mathfilters",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"allauth",
|
"allauth",
|
||||||
|
|||||||
@ -124,6 +124,7 @@ INSTALLED_APPS = [
|
|||||||
"videogames",
|
"videogames",
|
||||||
"locations",
|
"locations",
|
||||||
"webpages",
|
"webpages",
|
||||||
|
"lifeevents",
|
||||||
"mathfilters",
|
"mathfilters",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"allauth",
|
"allauth",
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user