import os from django.conf import settings as d_settings from django.db import models from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from aircox.conf import settings from .page import ChildPage from .program import ProgramChildQuerySet from .sound import Sound __all__ = ("Episode",) class EpisodeQuerySet(ProgramChildQuerySet): def with_podcasts(self): return self.filter(episodesound__sound__is_public=True, episodesound__sound__is_removed=False).distinct() class Episode(ChildPage): objects = EpisodeQuerySet.as_manager() detail_url_name = "episode-detail" list_url_name = "episode-list" edit_url_name = "episode-edit" template_prefix = "episode" @property def program(self): return self.parent_subclass @program.setter def program(self, value): self.parent = value @cached_property def podcasts(self): """Return serialized data about podcasts.""" query = self.episodesound_set.available().public().order_by("-broadcast", "position") return self._to_podcasts(query) @cached_property def sounds(self): """Return serialized data about all related sounds.""" query = self.episodesound_set.all().order_by("-broadcast", "position") return self._to_podcasts(query) def _to_podcasts(self, query): from ..serializers import EpisodeSoundSerializer as serializer_class query = query.select_related("sound") podcasts = [serializer_class(s).data for s in query] for index, podcast in enumerate(podcasts): podcasts[index]["page_url"] = self.get_absolute_url() podcasts[index]["page_title"] = self.title return podcasts class Meta: verbose_name = _("Episode") verbose_name_plural = _("Episodes") def save(self, *args, **kwargs): if self.parent is None: raise ValueError("missing parent program") super().save(*args, **kwargs) @classmethod def get_default_title(cls, page, date): return settings.EPISODE_TITLE.format( program=page, date=date.strftime(settings.EPISODE_TITLE_DATE_FORMAT), ) @classmethod def get_init_kwargs_from(cls, page, date=None, title=None, **kwargs): """Get default Episode's title.""" title = ( settings.EPISODE_TITLE.format( program=page, date=date.strftime(settings.EPISODE_TITLE_DATE_FORMAT), ) if title is None else title ) return super().get_init_kwargs_from(page, title=title, program=page, **kwargs) class EpisodeSoundQuerySet(models.QuerySet): def episode(self, episode): if isinstance(episode, int): return self.filter(episode_id=episode) return self.filter(episode=episode) def available(self): return self.filter(sound__is_removed=False) def public(self): return self.filter(sound__is_public=True) def broadcast(self): return self.available().filter(broadcast=True) def playlist(self, order="position"): # TODO: subquery expression if order: self = self.order_by(order) query = self.filter(sound__file__isnull=False, sound__is_removed=False).values_list("sound__file", flat=True) return [os.path.join(d_settings.MEDIA_ROOT, file) for file in query] class EpisodeSound(models.Model): """Element of an episode playlist.""" episode = models.ForeignKey(Episode, on_delete=models.CASCADE) sound = models.ForeignKey(Sound, on_delete=models.CASCADE) position = models.PositiveSmallIntegerField( _("order"), default=0, help_text=_("position in the playlist"), ) broadcast = models.BooleanField( _("Broadcast"), blank=None, help_text=_("The sound is broadcasted on air"), ) objects = EpisodeSoundQuerySet.as_manager() class Meta: verbose_name = _("Podcast") verbose_name_plural = _("Podcasts") def save(self, *args, **kwargs): if self.broadcast is None: self.broadcast = self.sound.broadcast super().save(*args, **kwargs)