[charts] Big revamp of the charts app
This commit is contained in:
138
vrobbler/apps/charts/models.py
Normal file
138
vrobbler/apps/charts/models.py
Normal file
@ -0,0 +1,138 @@
|
||||
import calendar
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
|
||||
User = get_user_model()
|
||||
BNULL = {"blank": True, "null": True}
|
||||
|
||||
|
||||
class ChartRecord(TimeStampedModel):
|
||||
"""Stores rankings for a user/media/time period combination.
|
||||
|
||||
Example entries:
|
||||
- Bjork was #3 artist in 2022
|
||||
- Homogenic was #10 album in April 2025
|
||||
- The Office was #1 TV series in Week #34 2024
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, **BNULL)
|
||||
|
||||
year = models.IntegerField()
|
||||
month = models.IntegerField(**BNULL)
|
||||
week = models.IntegerField(**BNULL)
|
||||
day = models.IntegerField(**BNULL)
|
||||
|
||||
artist = models.ForeignKey("music.Artist", on_delete=models.CASCADE, **BNULL)
|
||||
album = models.ForeignKey("music.Album", on_delete=models.CASCADE, **BNULL)
|
||||
track = models.ForeignKey("music.Track", on_delete=models.CASCADE, **BNULL)
|
||||
tv_series = models.ForeignKey("videos.Series", on_delete=models.CASCADE, **BNULL)
|
||||
video = models.ForeignKey("videos.Video", on_delete=models.CASCADE, **BNULL)
|
||||
podcast = models.ForeignKey("podcasts.Podcast", on_delete=models.CASCADE, **BNULL)
|
||||
podcast_episode = models.ForeignKey(
|
||||
"podcasts.PodcastEpisode", on_delete=models.CASCADE, **BNULL
|
||||
)
|
||||
board_game = models.ForeignKey(
|
||||
"boardgames.BoardGame", on_delete=models.CASCADE, **BNULL
|
||||
)
|
||||
trail = models.ForeignKey("trails.Trail", on_delete=models.CASCADE, **BNULL)
|
||||
geo_location = models.ForeignKey(
|
||||
"locations.GeoLocation", on_delete=models.CASCADE, **BNULL
|
||||
)
|
||||
food = models.ForeignKey("foods.Food", on_delete=models.CASCADE, **BNULL)
|
||||
book = models.ForeignKey("books.Book", on_delete=models.CASCADE, **BNULL)
|
||||
|
||||
rank = models.IntegerField()
|
||||
count = models.IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=["user", "year", "artist", "rank"]),
|
||||
models.Index(fields=["user", "year", "album", "rank"]),
|
||||
models.Index(fields=["user", "year", "track", "rank"]),
|
||||
models.Index(fields=["user", "year", "tv_series", "rank"]),
|
||||
models.Index(fields=["user", "year", "video", "rank"]),
|
||||
models.Index(fields=["user", "year", "podcast", "rank"]),
|
||||
models.Index(fields=["user", "year", "board_game", "rank"]),
|
||||
models.Index(fields=["user", "year", "trail", "rank"]),
|
||||
models.Index(fields=["user", "year", "geo_location", "rank"]),
|
||||
models.Index(fields=["user", "year", "food", "rank"]),
|
||||
models.Index(fields=["user", "year", "book", "rank"]),
|
||||
models.Index(fields=["user", "year", "week", "artist", "rank"]),
|
||||
models.Index(fields=["user", "year", "week", "tv_series", "rank"]),
|
||||
models.Index(fields=["user", "year", "month", "artist", "rank"]),
|
||||
models.Index(fields=["user", "year", "month", "tv_series", "rank"]),
|
||||
]
|
||||
|
||||
@property
|
||||
def media_obj(self):
|
||||
for attr in [
|
||||
"artist",
|
||||
"album",
|
||||
"track",
|
||||
"tv_series",
|
||||
"video",
|
||||
"podcast",
|
||||
"podcast_episode",
|
||||
"board_game",
|
||||
"trail",
|
||||
"geo_location",
|
||||
"food",
|
||||
"book",
|
||||
]:
|
||||
obj = getattr(self, attr)
|
||||
if obj:
|
||||
return obj
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_type(self):
|
||||
mapping = {
|
||||
"artist": "Artist",
|
||||
"album": "Album",
|
||||
"track": "Track",
|
||||
"tv_series": "TV Series",
|
||||
"video": "Video",
|
||||
"podcast": "Podcast",
|
||||
"podcast_episode": "Podcast Episode",
|
||||
"board_game": "Board Game",
|
||||
"trail": "Trail",
|
||||
"geo_location": "Location",
|
||||
"food": "Food",
|
||||
"book": "Book",
|
||||
}
|
||||
for attr, name in mapping.items():
|
||||
if getattr(self, attr) is not None:
|
||||
return name
|
||||
return None
|
||||
|
||||
@property
|
||||
def period_str(self) -> str:
|
||||
period = str(self.year)
|
||||
if self.month:
|
||||
period = f"{calendar.month_name[self.month]} {period}"
|
||||
if self.week:
|
||||
period = f"Week {self.week}, {period}"
|
||||
if self.day:
|
||||
period = f"{calendar.month_name[self.month]} {self.day}, {period}"
|
||||
return period
|
||||
|
||||
@property
|
||||
def period_type(self) -> str:
|
||||
if self.day:
|
||||
return "day"
|
||||
if self.week:
|
||||
return "week"
|
||||
if self.month:
|
||||
return "month"
|
||||
return "year"
|
||||
|
||||
def __str__(self):
|
||||
obj = self.media_obj
|
||||
obj_name = str(obj) if obj else "Unknown"
|
||||
return f"#{self.rank} {obj_name} in {self.period_str}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("charts:charts-home")
|
||||
Reference in New Issue
Block a user