229 lines
8.2 KiB
Python
229 lines
8.2 KiB
Python
"""News app models."""
|
|
from django.db import models
|
|
from django.contrib.contenttypes import fields as generic
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext_lazy as _
|
|
from rest_framework.reverse import reverse
|
|
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin, ProjectBaseMixin
|
|
from rating.models import Rating
|
|
|
|
|
|
class NewsType(models.Model):
|
|
"""NewsType model."""
|
|
|
|
name = models.CharField(_('name'), max_length=250)
|
|
tag_categories = models.ManyToManyField('tag.TagCategory',
|
|
related_name='news_types')
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
|
|
verbose_name_plural = _('news types')
|
|
verbose_name = _('news type')
|
|
|
|
def __str__(self):
|
|
"""Overrided __str__ method."""
|
|
return self.name
|
|
|
|
|
|
class NewsQuerySet(models.QuerySet):
|
|
"""QuerySet for model News"""
|
|
|
|
def rating_value(self):
|
|
return self.annotate(rating=models.Count('ratings__ip', distinct=True))
|
|
|
|
def with_base_related(self):
|
|
"""Return qs with related objects."""
|
|
return self.select_related('news_type', 'country').prefetch_related('tags')
|
|
|
|
def with_extended_related(self):
|
|
"""Return qs with related objects."""
|
|
return self.select_related('created_by', 'agenda', 'banner')
|
|
|
|
def by_type(self, news_type):
|
|
"""Filter News by type"""
|
|
return self.filter(news_type__name=news_type)
|
|
|
|
def by_tags(self, tags):
|
|
return self.filter(tags__in=tags)
|
|
|
|
def by_country_code(self, code):
|
|
"""Filter collection by country code."""
|
|
return self.filter(country__code=code)
|
|
|
|
def published(self):
|
|
"""Return only published news"""
|
|
now = timezone.now()
|
|
return self.filter(models.Q(models.Q(end__gte=now) |
|
|
models.Q(end__isnull=True)),
|
|
state__in=self.model.PUBLISHED_STATES, start__lte=now)
|
|
|
|
# todo: filter by best score
|
|
# todo: filter by country?
|
|
def should_read(self, news):
|
|
return self.model.objects.exclude(pk=news.pk).published(). \
|
|
with_base_related().by_type(news.news_type).distinct().order_by('?')
|
|
|
|
def same_theme(self, news):
|
|
return self.model.objects.exclude(pk=news.pk).published(). \
|
|
with_base_related().by_type(news.news_type). \
|
|
by_tags(news.tags.all()).distinct().order_by('-start')
|
|
|
|
|
|
class Agenda(ProjectBaseMixin, TranslatedFieldsMixin):
|
|
"""News agenda model"""
|
|
|
|
event_datetime = models.DateTimeField(default=timezone.now, editable=False,
|
|
verbose_name=_('Event datetime'))
|
|
address = models.ForeignKey('location.Address', blank=True, null=True,
|
|
default=None, verbose_name=_('address'),
|
|
on_delete=models.SET_NULL)
|
|
content = TJSONField(blank=True, null=True, default=None,
|
|
verbose_name=_('content'),
|
|
help_text='{"en-GB":"some text"}')
|
|
|
|
|
|
class NewsBanner(ProjectBaseMixin, TranslatedFieldsMixin):
|
|
"""News banner model"""
|
|
title = TJSONField(blank=True, null=True, default=None,
|
|
verbose_name=_('title'),
|
|
help_text='{"en-GB":"some text"}')
|
|
image_url = models.URLField(verbose_name=_('Image URL path'),
|
|
blank=True, null=True, default=None)
|
|
content_url = models.URLField(verbose_name=_('Content URL path'),
|
|
blank=True, null=True, default=None)
|
|
|
|
|
|
class News(BaseAttributes, TranslatedFieldsMixin):
|
|
"""News model."""
|
|
|
|
STR_FIELD_NAME = 'title'
|
|
|
|
# TEMPLATE CHOICES
|
|
NEWSPAPER = 0
|
|
MAIN_PDF_ERB = 1
|
|
MAIN = 2
|
|
|
|
TEMPLATE_CHOICES = (
|
|
(NEWSPAPER, 'newspaper'),
|
|
(MAIN_PDF_ERB, 'main.pdf.erb'),
|
|
(MAIN, 'main'),
|
|
)
|
|
|
|
# STATE CHOICES
|
|
WAITING = 0
|
|
HIDDEN = 1
|
|
PUBLISHED = 2
|
|
PUBLISHED_EXCLUSIVE = 3
|
|
|
|
PUBLISHED_STATES = [PUBLISHED, PUBLISHED_EXCLUSIVE]
|
|
|
|
STATE_CHOICES = (
|
|
(WAITING, _('Waiting')),
|
|
(HIDDEN, _('Hidden')),
|
|
(PUBLISHED, _('Published')),
|
|
(PUBLISHED_EXCLUSIVE, _('Published exclusive')),
|
|
)
|
|
|
|
news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT,
|
|
verbose_name=_('news type'))
|
|
title = TJSONField(blank=True, null=True, default=None,
|
|
verbose_name=_('title'),
|
|
help_text='{"en-GB":"some text"}')
|
|
subtitle = TJSONField(blank=True, null=True, default=None,
|
|
verbose_name=_('subtitle'),
|
|
help_text='{"en-GB":"some text"}')
|
|
description = TJSONField(blank=True, null=True, default=None,
|
|
verbose_name=_('description'),
|
|
help_text='{"en-GB":"some text"}')
|
|
start = models.DateTimeField(verbose_name=_('Start'))
|
|
end = models.DateTimeField(blank=True, null=True, default=None,
|
|
verbose_name=_('End'))
|
|
slug = models.SlugField(unique=True, max_length=255,
|
|
verbose_name=_('News slug'))
|
|
state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES,
|
|
verbose_name=_('State'))
|
|
is_highlighted = models.BooleanField(default=False,
|
|
verbose_name=_('Is highlighted'))
|
|
template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER)
|
|
address = models.ForeignKey('location.Address', blank=True, null=True,
|
|
default=None, verbose_name=_('address'),
|
|
on_delete=models.SET_NULL)
|
|
country = models.ForeignKey('location.Country', blank=True, null=True,
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_('country'))
|
|
tags = models.ManyToManyField('tag.Tag', related_name='news',
|
|
verbose_name=_('Tags'))
|
|
gallery = models.ManyToManyField('gallery.Image', through='news.NewsGallery')
|
|
ratings = generic.GenericRelation(Rating)
|
|
|
|
agenda = models.ForeignKey('news.Agenda', blank=True, null=True,
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_('agenda'))
|
|
|
|
banner = models.ForeignKey('news.NewsBanner', blank=True, null=True,
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_('banner'))
|
|
|
|
objects = NewsQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
|
|
verbose_name = _('news')
|
|
verbose_name_plural = _('news')
|
|
|
|
def __str__(self):
|
|
return f'news: {self.slug}'
|
|
|
|
@property
|
|
def is_publish(self):
|
|
return self.state in self.PUBLISHED_STATES
|
|
|
|
@property
|
|
def web_url(self):
|
|
return reverse('web:news:rud', kwargs={'slug': self.slug})
|
|
|
|
@property
|
|
def should_read(self):
|
|
return self.__class__.objects.should_read(self)[:3]
|
|
|
|
@property
|
|
def same_theme(self):
|
|
return self.__class__.objects.same_theme(self)[:3]
|
|
|
|
@property
|
|
def main_image(self):
|
|
qs = self.news_gallery.main_image()
|
|
if qs.exists():
|
|
return qs.first().image
|
|
|
|
|
|
class NewsGalleryQuerySet(models.QuerySet):
|
|
"""QuerySet for model News"""
|
|
|
|
def main_image(self):
|
|
"""Return objects with flag is_main is True"""
|
|
return self.filter(is_main=True)
|
|
|
|
|
|
class NewsGallery(models.Model):
|
|
|
|
news = models.ForeignKey(News, null=True,
|
|
related_name='news_gallery',
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_('news'))
|
|
image = models.ForeignKey('gallery.Image', null=True,
|
|
related_name='news_gallery',
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_('gallery'))
|
|
is_main = models.BooleanField(default=False,
|
|
verbose_name=_('Is the main image'))
|
|
|
|
objects = NewsGalleryQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""NewsGallery meta class."""
|
|
verbose_name = _('news gallery')
|
|
verbose_name_plural = _('news galleries')
|