Example #1
0
class Series(models.Model):
    name = models.CharField(max_length=200)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)
    uuid = models.UUIDField(default=uuid.uuid4, editable=False)
    description = extra.MarkdownTextField(blank=True, null=True)

    class Meta:
        app_label = 'alibrary'
        verbose_name = _('Series')
        verbose_name_plural = _('Series')
        ordering = ('-name', )

    def __unicode__(self):
        return '%s' % (self.name)
Example #2
0
class Profile(TimestampedModelMixin, UUIDModelMixin, MigrationMixin):

    GENDER_CHOICES = (
        (0, _('Male')),
        (1, _('Female')),
        (2, _('Other')),
    )
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                unique=True,
                                on_delete=models.CASCADE)

    mentor = models.ForeignKey(settings.AUTH_USER_MODEL,
                               blank=True,
                               null=True,
                               related_name="godchildren")

    #Personal
    gender = models.PositiveSmallIntegerField(_('gender'),
                                              choices=GENDER_CHOICES,
                                              blank=True,
                                              null=True)
    birth_date = models.DateField(_('Date of birth'),
                                  blank=True,
                                  null=True,
                                  help_text=_('Format: YYYY-MM-DD'))
    pseudonym = models.CharField(
        blank=True,
        null=True,
        max_length=250,
        help_text=_('Will appear instead of your first- & last name'))
    description = models.CharField(_('Disambiguation'),
                                   blank=True,
                                   null=True,
                                   max_length=250)
    biography = extra.MarkdownTextField(blank=True, null=True)

    image = models.ImageField(verbose_name=_('Profile Image'),
                              upload_to=filename_by_uuid,
                              null=True,
                              blank=True)

    # Contact (personal)
    mobile = PhoneNumberField(_('mobile'), blank=True, null=True)
    phone = PhoneNumberField(_('phone'), blank=True, null=True)
    fax = PhoneNumberField(_('fax'), blank=True, null=True)

    address1 = models.CharField(_('address'),
                                null=True,
                                blank=True,
                                max_length=100)
    address2 = models.CharField(_('address (secondary)'),
                                null=True,
                                blank=True,
                                max_length=100)
    city = models.CharField(_('city'), null=True, blank=True, max_length=100)
    zip = models.CharField(_('zip'), null=True, blank=True, max_length=10)
    country = models.ForeignKey(Country, blank=True, null=True)

    iban = models.CharField(_('IBAN'), null=True, blank=True, max_length=120)
    paypal = models.EmailField(_('Paypal'),
                               null=True,
                               blank=True,
                               max_length=200)

    # relations
    expertise = models.ManyToManyField('Expertise',
                                       verbose_name=_('Fields of expertise'),
                                       blank=True)

    # tagging (d_tags = "display tags")
    d_tags = tagging.fields.TagField(max_length=1024,
                                     verbose_name="Tags",
                                     blank=True,
                                     null=True)

    # alpha features
    enable_alpha_features = models.BooleanField(default=False)

    class Meta:
        app_label = 'profiles'
        verbose_name = _('user profile')
        verbose_name_plural = _('user profiles')
        db_table = 'user_profiles'
        ordering = ('-user__last_login', )

        permissions = (
            ('mentor_profiles', _('Mentoring profiles')),
            ('view_profiles_private', _('View private profile-data.')),
        )

    def __unicode__(self):
        return u"%s" % self.get_display_name()

    def get_full_name(self):
        if self.user:
            return self.user.get_full_name()

    @property
    def name(self):
        return self.get_display_name()

    @property
    def main_image(self):
        return self.image

    def get_display_name(self):

        if self.pseudonym:
            return self.pseudonym

        if self.user.get_full_name():
            return self.user.get_full_name()

        return self.user.username

    @property
    def is_approved(self):
        if self.user in Group.objects.get(name='Mentor').user_set.all():
            return True

        return

    def approve(self, mentor, level):

        groups_to_add = []

        if level == 'music_pro':
            groups_to_add = (
                'Music PRO',
                'Mentor',
            )

        if level == 'radio_pro':
            groups_to_add = (
                'Radio PRO',
                'Mentor',
            )

        groups = Group.objects.filter(name__in=groups_to_add)

        for group in groups:
            self.user.groups.add(group)

        self.user.groups.remove(Group.objects.get(name=DEFAULT_GROUP))

    @property
    def age(self):
        if self.birth_date:
            return u"%s" % relativedelta.relativedelta(datetime.date.today(),
                                                       self.birth_date).years
        else:
            return None

    def get_ct(self):
        return '{}.{}'.format(self._meta.app_label,
                              self.__class__.__name__).lower()

    def get_absolute_url(self):
        # return reverse('profiles-profile-detail-legacy', kwargs={ 'username': self.user.username })
        return reverse('profiles-profile-detail',
                       kwargs={'uuid': str(self.uuid)})

    @models.permalink
    def get_edit_url(self):
        return ('profiles-profile-edit', )

    def get_admin_url(self):
        return reverse("admin:profiles_profile_change", args=(self.pk, ))

    def get_api_url(self):
        return None
        # return reverse('api_dispatch_detail', kwargs={
        #     'api_name': 'v1',
        #     'resource_name': 'profile',
        #     'pk': self.pk
        # })

    def get_groups(self):
        return self.user.groups

    def save(self, *args, **kwargs):
        super(Profile, self).save(*args, **kwargs)
Example #3
0
class Community(UUIDModelMixin, MigrationMixin):

    name = models.CharField(max_length=200, db_index=True)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)

    group = models.OneToOneField(Group, unique=True, null=True, blank=True)
    members = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)

    # auto-update
    created = models.DateTimeField(auto_now_add=True, editable=False)
    updated = models.DateTimeField(auto_now=True, editable=False)

    # Profile
    description = extra.MarkdownTextField(blank=True, null=True)
    image = models.ImageField(verbose_name=_('Profile Image'),
                              upload_to=filename_by_uuid,
                              null=True,
                              blank=True)

    # Contact
    mobile = PhoneNumberField(_('mobile'), blank=True, null=True)
    phone = PhoneNumberField(_('phone'), blank=True, null=True)
    fax = PhoneNumberField(_('fax'), blank=True, null=True)
    email = models.EmailField(blank=True, null=True)

    address1 = models.CharField(_('address'),
                                null=True,
                                blank=True,
                                max_length=100)
    address2 = models.CharField(_('address (secondary)'),
                                null=True,
                                blank=True,
                                max_length=100)
    city = models.CharField(_('city'), null=True, blank=True, max_length=100)
    zip = models.CharField(_('zip'), null=True, blank=True, max_length=10)
    #country = CountryField(blank=True, null=True)
    country = models.ForeignKey(Country, blank=True, null=True)
    # relations
    expertise = models.ManyToManyField('Expertise',
                                       verbose_name=_('Fields of expertise'),
                                       blank=True)

    # tagging (d_tags = "display tags")
    d_tags = tagging.fields.TagField(verbose_name="Tags",
                                     blank=True,
                                     null=True)

    class Meta:
        app_label = 'profiles'
        verbose_name = _('Community')
        verbose_name_plural = _('Communities')
        """
        permissions = (
            ('mentor_profiles', 'Mentoring profiles'),
        )
        """

    def __unicode__(self):
        return u"%s" % self.name

    def save(self, *args, **kwargs):
        t_tags = ''
        """"""
        for tag in self.tags:
            t_tags += '%s, ' % tag

        self.tags = t_tags
        self.d_tags = t_tags[:245]

        super(Community, self).save(*args, **kwargs)
Example #4
0
class Distributor(MigrationMixin, UUIDModelMixin, TimestampedModelMixin,
                  models.Model):
    """
    TODO: suggest to remove 'distributor' implementation.
    it is not used at the moment (90 entries, last 2014...)
    """

    TYPE_CHOICES = (
        ('unknown', _('Unknown')),
        ('major', _('Major')),
        ('indy', _('Independent')),
        ('other', _('Other')),
    )

    type = models.CharField(verbose_name="Distributor type",
                            max_length=12,
                            default='unknown',
                            choices=TYPE_CHOICES)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)
    name = models.CharField(max_length=400)
    description = extra.MarkdownTextField(blank=True, null=True)
    code = models.CharField(max_length=50)
    address = models.TextField(blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    phone = PhoneNumberField(blank=True, null=True)
    fax = PhoneNumberField(blank=True, null=True)
    country = models.ForeignKey(Country, blank=True, null=True)

    # relations
    parent = models.ForeignKey('self',
                               null=True,
                               blank=True,
                               related_name='children')
    labels = models.ManyToManyField('Label',
                                    through='DistributorLabel',
                                    blank=True,
                                    related_name="distributors")
    relations = GenericRelation('Relation')
    d_tags = tagging.fields.TagField(verbose_name="Tags",
                                     max_length=1024,
                                     blank=True,
                                     null=True)

    objects = models.Manager()

    class Meta:
        app_label = 'alibrary'
        verbose_name = _('Distributor')
        verbose_name_plural = _('Distributors')
        ordering = ('name', )

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('alibrary-distributor-detail', [self.slug])

    @models.permalink
    def get_edit_url(self):
        return ('alibrary-distributor-edit', [self.pk])

    def save(self, *args, **kwargs):
        unique_slugify(self, self.name)

        t_tags = ''
        for tag in self.tags:
            t_tags += '%s, ' % tag

        self.tags = t_tags
        self.d_tags = t_tags

        super(Distributor, self).save(*args, **kwargs)
Example #5
0
class Playlist(MigrationMixin, TimestampedModelMixin, models.Model):

    TYPE_BASKET = 'basket'
    TYPE_PLAYLIST = 'playlist'
    TYPE_BROADCAST = 'broadcast'
    TYPE_OTHER = 'other'

    TYPE_CHOICES = (
        (TYPE_BASKET, _('Private Playlist')),
        (TYPE_PLAYLIST, _('Public Playlist')),
        (TYPE_BROADCAST, _('Broadcast')),
        (TYPE_OTHER, _('Other')),
    )

    name = models.CharField(max_length=200)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)
    uuid = models.UUIDField(default=uuid.uuid4, editable=False)

    status = models.PositiveIntegerField(
        default=0, choices=alibrary_settings.PLAYLIST_STATUS_CHOICES)
    type = models.CharField(max_length=12,
                            default='basket',
                            null=True,
                            choices=TYPE_CHOICES)
    broadcast_status = models.PositiveIntegerField(
        default=0, choices=alibrary_settings.PLAYLIST_BROADCAST_STATUS_CHOICES)
    broadcast_status_messages = JSONField(blank=True, null=True, default=None)

    playout_mode_random = models.BooleanField(
        verbose_name=_('Shuffle Playlist'),
        default=False,
        help_text=_(
            'If enabled the order of the tracks will be randomized for playout'
        ))

    rotation = models.BooleanField(default=True)
    rotation_date_start = models.DateField(verbose_name=_('Rotate from'),
                                           blank=True,
                                           null=True)
    rotation_date_end = models.DateField(verbose_name=_('Rotate until'),
                                         blank=True,
                                         null=True)

    main_image = models.ImageField(verbose_name=_('Image'),
                                   upload_to=upload_image_to,
                                   storage=OverwriteStorage(),
                                   null=True,
                                   blank=True)

    # relations
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             null=True,
                             blank=True,
                             default=None,
                             related_name='playlists')
    items = models.ManyToManyField('PlaylistItem',
                                   through='PlaylistItemPlaylist',
                                   blank=True)

    # tagging (d_tags = "display tags")
    d_tags = tagging.fields.TagField(max_length=1024,
                                     verbose_name='Tags',
                                     blank=True,
                                     null=True)

    # updated/calculated on save
    duration = models.IntegerField(null=True, default=0)

    target_duration = models.PositiveIntegerField(
        default=0,
        null=True,
        choices=alibrary_settings.PLAYLIST_TARGET_DURATION_CHOICES)

    dayparts = models.ManyToManyField(Daypart,
                                      blank=True,
                                      related_name='daypart_plalists')
    seasons = models.ManyToManyField('Season',
                                     blank=True,
                                     related_name='season_plalists')
    weather = models.ManyToManyField('Weather',
                                     blank=True,
                                     related_name='weather_plalists')

    # series
    series = models.ForeignKey(Series,
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL)
    series_number = models.PositiveIntegerField(null=True, blank=True)

    # is currently selected as default?
    is_current = models.BooleanField(_('Currently selected?'), default=False)

    description = extra.MarkdownTextField(blank=True, null=True)

    mixdown_file = models.FileField(null=True,
                                    blank=True,
                                    upload_to=upload_mixdown_to)

    emissions = GenericRelation('abcast.Emission')

    # meta
    class Meta:
        app_label = 'alibrary'
        verbose_name = _('Playlist')
        verbose_name_plural = _('Playlists')
        ordering = ('-updated', )

        permissions = (
            ('view_playlist', 'View Playlist'),
            ('edit_playlist', 'Edit Playlist'),
            ('schedule_playlist', 'Schedule Playlist'),
            ('admin_playlist', 'Edit Playlist (extended)'),
        )

    def __unicode__(self):
        return self.name

    def get_ct(self):
        return '{}.{}'.format(self._meta.app_label,
                              self.__class__.__name__).lower()

    def get_absolute_url(self):
        return reverse('alibrary-playlist-detail',
                       kwargs={
                           'slug': self.slug,
                       })

    def get_edit_url(self):
        return reverse("alibrary-playlist-edit", args=(self.pk, ))

    def get_delete_url(self):
        return reverse("alibrary-playlist-delete", args=(self.pk, ))

    def get_admin_url(self):
        return reverse("admin:alibrary_playlist_change", args=(self.pk, ))

    def get_duration(self):
        duration = 0
        try:
            for item in self.items.all():
                duration += item.content_object.get_duration()
                pip = PlaylistItemPlaylist.objects.get(playlist=self,
                                                       item=item)
                duration -= pip.cue_in
                duration -= pip.cue_out
                duration -= pip.fade_cross
        except:
            pass

        return duration

    # TODO: remove usages and use generic reverse 'emissions' instead
    def get_emissions(self):
        from abcast.models import Emission
        ctype = ContentType.objects.get_for_model(self)
        emissions = Emission.objects.filter(
            content_type__pk=ctype.id,
            object_id=self.id).order_by('-time_start')
        return emissions

    def get_api_url(self):
        return reverse('api_dispatch_detail',
                       kwargs={
                           'api_name': 'v1',
                           'resource_name': 'library/playlist',
                           'pk': self.pk
                       }) + ''

    def get_api_simple_url(self):
        return reverse('api_dispatch_detail',
                       kwargs={
                           'api_name': 'v1',
                           'resource_name': 'library/simpleplaylist',
                           'pk': self.pk
                       }) + ''

    def can_be_deleted(self):

        can_delete = False
        reason = _('This playlist cannot be deleted.')

        if self.type == 'basket':
            can_delete = True
            reason = None

        if self.type == 'playlist':
            can_delete = False
            reason = _(
                'Playlist "%s" is public. It cannot be deleted anymore.' %
                self.name)

        if self.type == 'broadcast':
            can_delete = False
            reason = _(
                'Playlist "%s" published for broadcast. It cannot be deleted anymore.'
                % self.name)

        return can_delete, reason

    def get_transform_status(self, target_type):
        """
        check if transformation is possible /
        what needs to be done
        Not so nicely here - but...
        """

        status = False
        """
        criterias = [
            {
                'key': 'tags',
                'name': _('Tags'),
                'status': True,
                'warning': _('Please add some tags'),
            },
            {
                'key': 'description',
                'name': _('Description'),
                'status': False,
                'warning': _('Please add a description'),
            }
        ]
        """

        criterias = []

        # "basket" only used while dev...
        if target_type == 'basket':
            status = True

        if target_type == 'playlist':
            status = True
            # tags
            tag_count = self.tags.count()
            if tag_count < 1:
                status = False
            criteria = {
                'key': 'tags',
                'name': _('Tags'),
                'status': tag_count > 0,
                'warning': _('Please add some tags'),
            }
            criterias.append(criteria)
            # scheduled
            if self.type == 'broadcast':
                schedule_count = self.get_emissions().count()
                if schedule_count > 0:
                    status = False
                criteria = {
                    'key':
                    'scheduled',
                    'name':
                    _('Playlist already scheduled')
                    if schedule_count > 0 else _('Playlist not scheduled'),
                    'status':
                    schedule_count < 1,
                    'warning':
                    _('This playlist has already ben scheduled %s times. Remove all scheduler entries to "un-broadcast" this playlist.'
                      % schedule_count),
                }
                if schedule_count > 0:
                    criterias.append(criteria)

        if target_type == 'broadcast':
            status = True
            # tags
            tag_count = self.tags.count()
            if tag_count < 1:
                status = False
            criteria = {
                'key': 'tags',
                'name': _('Tags'),
                'status': tag_count > 0,
                'warning': _('Please add some tags'),
            }
            criterias.append(criteria)

            # dayparts
            dp_count = self.dayparts.count()
            if not dp_count:
                status = False
            criteria = {
                'key': 'dayparts',
                'name': _('Dayparts'),
                'status': dp_count > 0,
                'warning': _('Please specify the dayparts'),
            }
            criterias.append(criteria)

            # duration
            if not self.broadcast_status == 1:
                status = False
            criteria = {
                'key': 'duration',
                'name': _('Duration'),
                'status': True if self.broadcast_status == 1 else False,
                'warning': _('Durations do not match'),
                # 'warning': ', '.join(self.broadcast_status_messages),
            }
            criterias.append(criteria)

        transformation = {'criterias': criterias, 'status': status}

        return transformation

    ###################################################################
    # legacy version - used in tastypie API (v1)
    ###################################################################
    def add_items_by_ids(self, ids, ct, timing=None):

        from alibrary.models.mediamodels import Media

        log.debug('add media to playlist: {}'.format(', '.join(ids)))

        for id in ids:
            id = int(id)

            co = None

            if ct == 'media':
                co = Media.objects.get(pk=id)

            if co:

                i = PlaylistItem(content_object=co)
                i.save()
                """
                ctype = ContentType.objects.get_for_model(co)
                item, created = PlaylistItem.objects.get_or_create(object_id=co.pk, content_type=ctype)
                """

                pi, created = PlaylistItemPlaylist.objects.get_or_create(
                    item=i, playlist=self, position=self.items.count())

                if timing:
                    try:
                        pi.fade_in = timing['fade_in']
                        pi.fade_out = timing['fade_out']
                        pi.cue_in = timing['cue_in']
                        pi.cue_out = timing['cue_out']
                        pi.save()
                    except:
                        pass

        self.save()

    ###################################################################
    # new version - used in DRF API (v245)
    ###################################################################
    def add_item(self, item, cue_and_fade=None, commit=True):

        log.debug('add item to playlist: {}'.format(item))

        playlist_item = PlaylistItem(content_object=item)
        playlist_item.save()

        playlist_item_playlist = PlaylistItemPlaylist(
            item=playlist_item, playlist=self, position=self.items.count())

        if cue_and_fade:
            playlist_item_playlist.fade_in = cue_and_fade['fade_in']
            playlist_item_playlist.fade_out = cue_and_fade['fade_out']
            playlist_item_playlist.cue_in = cue_and_fade['cue_in']
            playlist_item_playlist.cue_out = cue_and_fade['cue_out']

        playlist_item_playlist.save()

        if commit:
            self.save()

    def reorder_items_by_uuids(self, uuids):

        i = 0

        for uuid in uuids:
            pi = PlaylistItemPlaylist.objects.get(uuid=uuid)
            pi.position = i
            pi.save()

            i += 1

        self.save()

    def convert_to(self, playlist_type):

        log.debug('requested to convert "%s" from %s to %s' %
                  (self.name, self.type, playlist_type))

        if playlist_type == 'broadcast':
            self.broadcast_status, self.broadcast_status_messages = self.self_check(
            )

        transformation = self.get_transform_status(playlist_type)
        status = transformation['status']

        if playlist_type == 'broadcast' and status:
            _status, messages = self.self_check()
            if _status == 1:
                status = True

        if status:
            self.type = playlist_type
            self.created = timezone.now()
            self.save()

        return self, status

    def get_items(self):

        pis = PlaylistItemPlaylist.objects.filter(
            playlist=self).order_by('position')

        items = []
        for pi in pis:
            item = pi.item
            item.cue_in = pi.cue_in
            item.cue_out = pi.cue_out
            item.fade_in = pi.fade_in
            item.fade_out = pi.fade_out
            item.fade_cross = pi.fade_cross
            # get the actual playout duration
            try:
                # print '// getting duration for:'
                # print '%s - %s' % (item.content_object.pk, item.content_object.name)
                # print 'obj duration: %s' % item.content_object.duration_s
                item.playout_duration = item.content_object.duration_ms - item.cue_in - item.cue_out - item.fade_cross
            except Exception as e:
                log.warning('unable to get duration: {}'.format(e))
                item.playout_duration = 0

            items.append(item)
        return items

    def self_check(self):
        """
        check if everything is fine to be 'scheduled'
        """

        log.info('Self check requested for: %s' % self.name)

        status = 1  # set to 'OK'
        messages = []

        try:
            # check ready-status of related media
            for item in self.items.all():
                # log.debug('Self check content object: %s' % item.content_object)
                # log.debug('Self check master: %s' % item.content_object.master)
                # log.debug('Self check path: %s' % item.content_object.master.path)

                # check if file available
                try:
                    with open(item.content_object.master.path):
                        pass
                except IOError as e:
                    log.warning(
                        _('File does not exists: %s | %s') %
                        (e, item.content_object.master.path))
                    status = 99
                    messages.append(
                        _('File does not exists: %s | %s') %
                        (e, item.content_object.master.path))
                """
                pip = PlaylistItemPlaylist.objects.get(playlist=self, item=item)
                duration -= pip.cue_in
                duration -= pip.cue_out
                duration -= pip.fade_cross
                """

            # check duration & matching target_duration
            """
            compare durations. target: in seconds | calculated duration in milliseconds
            """
            diff = self.get_duration() - self.target_duration * 1000
            if abs(diff) > DURATION_MAX_DIFF:
                messages.append(
                    _('durations do not match. difference is: %s seconds' %
                      int(diff / 1000)))
                log.warning(
                    'durations do not match. difference is: %s seconds' %
                    int(diff / 1000))
                status = 2

        except Exception as e:
            messages.append(_('Validation error: %s ' % e))
            log.warning('validation error: %s ' % e)
            status = 99

        if status == 1:
            log.info('Playlist "%s" checked - all fine!' % (self.name))

        return status, messages

    ###################################################################
    # playlist mixdown
    ###################################################################
    def get_mixdown(self):
        """
        get mixdown from api
        """
        return MixdownAPIClient().get_for_playlist(self)

    def request_mixdown(self):
        """
        request (re-)creation of mixdown
        """
        return MixdownAPIClient().request_for_playlist(self)

    def download_mixdown(self):
        """
        download generated mixdown from api & store locally (in `mixdown_file` field)
        """
        if not self.mixdown:
            log.info('mixdown not available on api')
            return

        if not self.mixdown['status'] == 3:
            log.info('mixdown not ready on api')
            return

        url = self.mixdown['mixdown_file']

        log.debug('download mixdown from api: {} > {}'.format(url, self.name))

        f_temp = NamedTemporaryFile(delete=True)
        f_temp.write(urlopen(url).read())
        f_temp.flush()

        # wipe existing file
        try:
            self.mixdown_file.delete(False)
        except IOError:
            pass

        self.mixdown_file.save(url.split('/')[-1], File(f_temp))

        return MixdownAPIClient().request_for_playlist(self)

    @property
    def sorted_items(self):
        return self.items.order_by('playlist_items__position')

    @cached_property
    def num_media(self):
        return self.items.count()

    @cached_property
    def mixdown(self):
        return self.get_mixdown()

    # provide type-based properties
    @property
    def is_broadcast(self):
        return self.type == Playlist.TYPE_BROADCAST

    @property
    def is_playlist(self):
        return self.type == Playlist.TYPE_PLAYLIST

    @cached_property
    def is_archived(self):
        if not self.type == Playlist.TYPE_BROADCAST:
            return
        if self.rotation_date_end and self.rotation_date_end < timezone.now(
        ).date():
            return True

    @cached_property
    def is_upcoming(self):
        if not self.type == Playlist.TYPE_BROADCAST:
            return
        if self.rotation_date_start and self.rotation_date_start > timezone.now(
        ).date():
            return True

    @cached_property
    def series_display(self):
        if not self.series:
            return
        if self.series_number:
            return '{} #{}'.format(self.series.name, self.series_number)
        return self.series.name

    @cached_property
    def last_emission(self):
        ###############################################################
        # we cannot filter a prefetched qs - so to avoid
        # additional queries we have to loop the qs and 'filter'
        # 'manually'
        ###############################################################
        for emission in self.emissions.order_by('-time_start'):
            if emission.time_start < timezone.now():
                return emission

    @cached_property
    def next_emission(self):
        ###############################################################
        # we cannot filter a prefetched qs - so to avoid
        # additional queries we have to loop the qs and 'filter'
        # 'manually'
        ###############################################################
        for emission in self.emissions.order_by('time_start'):
            if emission.time_start > timezone.now():
                return emission

    def save(self, *args, **kwargs):

        # status update
        if self.status == 0:
            self.status = 2

        duration = 0
        try:
            duration = self.get_duration()

        except Exception as e:
            pass

        self.duration = duration

        # TODO: maybe move
        self.broadcast_status, self.broadcast_status_messages = self.self_check(
        )
        if self.broadcast_status == 1:
            self.status = 1  # 'ready'
        else:
            self.status = 99  # 'error'

        super(Playlist, self).save(*args, **kwargs)
Example #6
0
class Release(MigrationMixin, UUIDModelMixin, TimestampedModelMixin,
              models.Model):

    # core fields
    name = models.CharField(max_length=200, db_index=True)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)
    license = models.ForeignKey(License,
                                blank=True,
                                null=True,
                                related_name='release_license')
    release_country = models.ForeignKey(Country, blank=True, null=True)

    main_image = models.ImageField(verbose_name=_('Cover'),
                                   upload_to=upload_cover_to,
                                   storage=OverwriteStorage(),
                                   null=True,
                                   blank=True)
    catalognumber = models.CharField(max_length=50, blank=True, null=True)
    """
    releasedate stores the 'real' time, approx is for inputs
    lik 2012-12 etc.
    """
    releasedate = models.DateField(blank=True, null=True)
    releasedate_approx = ApproximateDateField(verbose_name="Releasedate",
                                              blank=True,
                                              null=True)
    pressings = models.PositiveIntegerField(default=0)
    TOTALTRACKS_CHOICES = ((x, x) for x in range(1, 301))
    totaltracks = models.IntegerField(verbose_name=_('Total Tracks'),
                                      blank=True,
                                      null=True,
                                      choices=TOTALTRACKS_CHOICES)
    asin = models.CharField(max_length=150, blank=True)
    RELEASESTATUS_CHOICES = (
        (None, _('Not set')),
        ('official', _('Official')),
        ('promo', _('Promo')),
        ('bootleg', _('Bootleg')),
        ('other', _('Other')),
    )

    releasestatus = models.CharField(max_length=60,
                                     blank=True,
                                     choices=RELEASESTATUS_CHOICES)
    excerpt = models.TextField(blank=True, null=True)
    description = extra.MarkdownTextField(blank=True, null=True)
    releasetype = models.CharField(
        verbose_name="Release type",
        max_length=24,
        blank=True,
        null=True,
        choices=alibrary_settings.RELEASETYPE_CHOICES)
    label = models.ForeignKey('alibrary.Label',
                              blank=True,
                              null=True,
                              related_name='release_label',
                              on_delete=models.SET_NULL)
    media = models.ManyToManyField('alibrary.Media',
                                   through='ReleaseMedia',
                                   blank=True,
                                   related_name="releases")

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              blank=True,
                              null=True,
                              related_name="releases_owner",
                              on_delete=models.SET_NULL)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL,
                                blank=True,
                                null=True,
                                related_name="releases_creator",
                                on_delete=models.SET_NULL)
    last_editor = models.ForeignKey(settings.AUTH_USER_MODEL,
                                    blank=True,
                                    null=True,
                                    related_name="releases_last_editor",
                                    on_delete=models.SET_NULL)
    publisher = models.ForeignKey(settings.AUTH_USER_MODEL,
                                  blank=True,
                                  null=True,
                                  related_name="releases_publisher",
                                  on_delete=models.SET_NULL)

    barcode = models.CharField(max_length=32, blank=True, null=True)

    extra_artists = models.ManyToManyField('alibrary.Artist',
                                           through='ReleaseExtraartists',
                                           blank=True)
    album_artists = models.ManyToManyField('alibrary.Artist',
                                           through='ReleaseAlbumartists',
                                           related_name="release_albumartists",
                                           blank=True)

    relations = GenericRelation(Relation)
    d_tags = tagging.fields.TagField(max_length=1024,
                                     verbose_name="Tags",
                                     blank=True,
                                     null=True)

    objects = ReleaseManager()

    class Meta:
        app_label = 'alibrary'
        verbose_name = _('Release')
        verbose_name_plural = _('Releases')
        ordering = ('-created', )

        permissions = (
            ('view_release', 'View Release'),
            ('edit_release', 'Edit Release'),
            ('merge_release', 'Merge Releases'),
            ('admin_release', 'Edit Release (extended)'),
        )

    def __unicode__(self):
        return self.name

    @property
    def classname(self):
        return self.__class__.__name__

    @property
    def publish_date(self):
        # compatibility hack TODO: refactor all dependencies
        return datetime.utcnow()

    def is_active(self):
        now = date.today()
        try:
            if not self.releasedate:
                return True

            if self.releasedate <= now:
                return True

        except:
            pass

        return False

    @property
    def is_promotional(self):
        # TODO: refactor query to reduce db hits
        if self.releasedate:
            if self.releasedate > datetime.now().date():
                return True

        if License.objects.filter(media_license__in=self.get_media(),
                                  is_promotional=True).distinct().exists():
            return True

        return False

    @property
    def is_new(self):
        if self.is_promotional:
            return False
        if self.releasedate and self.releasedate >= (
                datetime.now() - timedelta(days=14)).date():
            return True

        return False

    def get_lookup_providers(self):

        providers = []
        for key, name in LOOKUP_PROVIDERS:
            relations = self.relations.filter(service=key)
            relation = None
            if relations.count() == 1:
                relation = relations[0]

            providers.append({'key': key, 'name': name, 'relation': relation})

        return providers

    def get_ct(self):
        return '{}.{}'.format(self._meta.app_label,
                              self.__class__.__name__).lower()

    def get_absolute_url(self):
        try:
            return reverse('alibrary-release-detail',
                           kwargs={
                               'pk': self.pk,
                               'slug': self.slug,
                           })
        except NoReverseMatch:
            translation.activate('en')
            return reverse('alibrary-release-detail',
                           kwargs={
                               'pk': self.pk,
                               'slug': self.slug,
                           })

    def get_edit_url(self):
        return reverse("alibrary-release-edit", args=(self.pk, ))

    def get_admin_url(self):
        return reverse("admin:alibrary_release_change", args=(self.pk, ))

    def get_api_url(self):
        return reverse('api_dispatch_detail',
                       kwargs={
                           'api_name': 'v1',
                           'resource_name': 'library/release',
                           'pk': self.pk
                       }) + ''

    def get_api_simple_url(self):
        return reverse('api_dispatch_detail',
                       kwargs={
                           'api_name': 'v1',
                           'resource_name': 'library/simplerelease',
                           'pk': self.pk
                       }) + ''

    def get_media(self):
        from alibrary.models import Media
        return Media.objects.filter(release=self).select_related(
            'artist', 'preflight_check')

    def get_products(self):
        return self.releaseproduct.all()

    def get_media_indicator(self):

        media = self.get_media()
        indicator = []

        if self.totaltracks:
            for i in range(self.totaltracks):
                indicator.append(0)

            for m in media:
                try:
                    indicator[m.tracknumber - 1] = 3
                except Exception as e:
                    pass

        else:
            for m in media:
                indicator.append(2)

        return indicator

    def get_license(self):

        licenses = License.objects.filter(
            media_license__in=self.get_media()).distinct()
        if not licenses.exists():
            return {'name': _(u'Not Defined')}

        if licenses.count() > 1:
            license, created = License.objects.get_or_create(name="Multiple")
            return license

        if licenses.count() == 1:
            return licenses[0]

    """
    compose artist display as string
    """

    def get_artist_display(self):

        artist_str = ''
        artists = self.get_artists()
        if len(artists) > 1:
            try:
                for artist in artists:
                    if artist['join_phrase']:
                        artist_str += ' %s ' % artist['join_phrase']
                    artist_str += artist['artist'].name
            except:
                artist_str = artists[0]['artist'].name
        else:
            try:
                artist_str = artists[0]['artist'].name
            except:
                try:
                    artist_str = artists[0].name
                except:
                    artist_str = _('Unknown Artist')

        return artist_str

    def get_artists(self):

        artists = []
        if self.album_artists.count() > 0:
            for albumartist in self.release_albumartist_release.all():
                artists.append({
                    'artist': albumartist.artist,
                    'join_phrase': albumartist.join_phrase
                })
            return artists

        medias = self.get_media()
        for media in medias:
            artists.append(media.artist)

        artists = list(set(artists))
        if len(artists) > 1:
            from alibrary.models import Artist
            a, c = Artist.objects.get_or_create(name="Various Artists")
            artists = [a]

        return artists

    def get_extra_artists(self):

        artists = []

        roles = ReleaseExtraartists.objects.filter(release=self.pk)

        for role in roles:
            try:
                role.artist.profession = role.profession.name
                artists.append(role.artist)
            except:
                pass

        return artists

    def get_downloads(self):
        return None

    def get_download_url(self, format, version):

        return '%sdownload/%s/%s/' % (self.get_absolute_url(), format, version)

    def get_cache_file_path(self, format, version):

        tmp_directory = TEMP_DIR
        file_name = '%s_%s_%s.%s' % (format, version, str(self.uuid), 'zip')
        tmp_path = '%s/%s' % (tmp_directory, file_name)

        return tmp_path

    def clear_cache_file(self):

        tmp_directory = TEMP_DIR
        pattern = '*%s.zip' % (str(self.uuid))
        versions = glob.glob('%s/%s' % (tmp_directory, pattern))

        try:
            for version in versions:
                os.remove(version)

        except Exception as e:
            pass

    def get_cache_file(self, format, version):

        cache_file_path = self.get_cache_file_path(format, version)

        if os.path.isfile(cache_file_path):
            logger.info('serving from cache: %s' % (cache_file_path))
            return cache_file_path

        else:
            return self.build_cache_file(format, version)

    def build_cache_file(self, format, version):

        cache_file_path = self.get_cache_file_path(format, version)

        logger.info('building cache for: %s' % (cache_file_path))

        try:
            os.remove(cache_file_path)

        except Exception as e:
            pass

        archive_file = ZipFile(cache_file_path, "w")
        """
        adding corresponding media files
        """
        for media in self.get_media():
            media_cache_file = media.inject_metadata(format, version)

            # filename for the file archive
            file_name = '%02d - %s - %s' % (media.tracknumber,
                                            media.artist.name, media.name)
            file_name = '%s.%s' % (file_name.encode('ascii', 'ignore'), format)

            archive_file.write(media_cache_file.path, file_name)

        return cache_file_path

    def get_extraimages(self):
        return None

    # OBSOLETE
    def complete_by_mb_id(self, mb_id):

        obj = self

        log = logging.getLogger('alibrary.release.complete_by_mb_id')
        log.info('complete release, r: %s | mb_id: %s' % (obj.name, mb_id))

        inc = ('artists', 'url-rels', 'aliases', 'tags', 'recording-rels',
               'work-rels', 'work-level-rels', 'artist-credits')
        url = 'http://%s/ws/2/release/%s/?fmt=json&inc=%s' % (
            MUSICBRAINZ_HOST, mb_id, "+".join(inc))

        r = requests.get(url)
        result = r.json()

        return obj

    def save(self, *args, **kwargs):

        self.clear_cache_file()
        unique_slugify(self, self.name)

        # convert approx date to real one
        ad = self.releasedate_approx
        try:
            ad_y = ad.year
            ad_m = ad.month
            ad_d = ad.day
            if ad_m == 0:
                ad_m = 1
            if ad_d == 0:
                ad_d = 1

            rd = datetime.strptime('%s/%s/%s' % (ad_y, ad_m, ad_d), '%Y/%m/%d')
            self.releasedate = rd
        except:
            self.releasedate = None

        if hasattr(self, '_last_editor') and getattr(self, '_last_editor'):
            last_editor = getattr(self, '_last_editor')
            self.last_editor = last_editor
        else:
            last_editor = None

        logger.debug('saved release id: {} - user: {} - caller: {}'.format(
            self.pk, last_editor,
            inspect.stack()[1][3]))

        super(Release, self).save(*args, **kwargs)
Example #7
0
class Label(MigrationMixin, UUIDModelMixin, TimestampedModelMixin,
            models.Model):

    name = models.CharField(max_length=400)
    slug = AutoSlugField(populate_from='name',
                         editable=True,
                         blank=True,
                         overwrite=True)
    labelcode = models.CharField(max_length=250, blank=True, null=True)
    address = models.TextField(blank=True, null=True)
    country = models.ForeignKey(Country, blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    phone = PhoneNumberField(blank=True, null=True)
    fax = PhoneNumberField(blank=True, null=True)
    main_image = models.ImageField(verbose_name=_('Logo Image'),
                                   upload_to=upload_image_to,
                                   storage=OverwriteStorage(),
                                   null=True,
                                   blank=True)
    description = extra.MarkdownTextField(blank=True, null=True)
    date_start = ApproximateDateField(verbose_name="Life-span begin",
                                      blank=True,
                                      null=True)
    date_end = ApproximateDateField(verbose_name="Life-span end",
                                    blank=True,
                                    null=True)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              blank=True,
                              null=True,
                              on_delete=models.SET_NULL,
                              related_name="labels_owner")
    creator = models.ForeignKey(settings.AUTH_USER_MODEL,
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL,
                                related_name="labels_creator")
    last_editor = models.ForeignKey(settings.AUTH_USER_MODEL,
                                    blank=True,
                                    null=True,
                                    on_delete=models.SET_NULL,
                                    related_name="labels_last_editor")
    publisher = models.ForeignKey(settings.AUTH_USER_MODEL,
                                  blank=True,
                                  null=True,
                                  on_delete=models.SET_NULL,
                                  related_name="labels_publisher")
    listed = models.BooleanField(
        verbose_name='Include in listings',
        default=True,
        help_text=_('Should this Label be shown on the default Label-list?'))
    disable_link = models.BooleanField(
        verbose_name='Disable Link',
        default=False,
        help_text=_('Disable Linking. Useful e.g. for "Unknown Label"'))
    disable_editing = models.BooleanField(
        verbose_name='Disable Editing',
        default=False,
        help_text=_('Disable Editing. Useful e.g. for "Unknown Label"'))
    type = models.CharField(verbose_name="Label type",
                            max_length=128,
                            default='unknown',
                            choices=alibrary_settings.LABELTYPE_CHOICES)
    relations = GenericRelation('Relation')
    d_tags = tagging.fields.TagField(verbose_name="Tags",
                                     max_length=1024,
                                     blank=True,
                                     null=True)

    # refactoring parent handling
    parent = models.ForeignKey('self',
                               null=True,
                               blank=True,
                               related_name='children')
    # identifiers
    ipi_code = models.CharField(verbose_name=_('IPI Code'),
                                max_length=32,
                                blank=True,
                                null=True)
    isni_code = models.CharField(verbose_name=_('ISNI Code'),
                                 max_length=32,
                                 blank=True,
                                 null=True)

    objects = LabelManager()

    class Meta:
        app_label = 'alibrary'
        verbose_name = _('Label')
        verbose_name_plural = _('Labels')
        ordering = ('name', )

        permissions = (('merge_label', 'Merge Labels'), )

    def __unicode__(self):
        return self.name

    def get_folder(self, name):
        return

    def get_lookup_providers(self):

        providers = []
        for key, name in LOOKUP_PROVIDERS:
            relations = self.relations.filter(service=key)
            relation = None
            if relations.count() == 1:
                relation = relations[0]

            providers.append({'key': key, 'name': name, 'relation': relation})

        return providers

    def get_ct(self):
        return '{}.{}'.format(self._meta.app_label,
                              self.__class__.__name__).lower()

    def get_absolute_url(self):
        if self.disable_link:
            return None
        try:
            return reverse('alibrary-label-detail',
                           kwargs={
                               'pk': self.pk,
                               'slug': self.slug,
                           })
        except NoReverseMatch:
            translation.activate('en')
            return reverse('alibrary-label-detail',
                           kwargs={
                               'pk': self.pk,
                               'slug': self.slug,
                           })

    def get_edit_url(self):
        return reverse("alibrary-label-edit", args=(self.pk, ))

    def get_admin_url(self):
        return reverse("admin:alibrary_label_change", args=(self.pk, ))

    def get_api_url(self):
        return reverse('api_dispatch_detail',
                       kwargs={
                           'api_name': 'v1',
                           'resource_name': 'library/label',
                           'pk': self.pk
                       }) + ''

    def get_root(self):

        if not self.parent:
            return None

        if self.parent == self:
            return None

        parent = self.parent
        last_parent = None
        i = 0
        while parent and i < 10:
            i += 1
            parent = parent.parent
            if parent:
                last_parent = parent

        return last_parent

    def save(self, *args, **kwargs):
        unique_slugify(self, self.name)
        super(Label, self).save(*args, **kwargs)