415 lines
15 KiB
Python
415 lines
15 KiB
Python
import re
|
|
from mptt.models import MPTTModel, TreeForeignKey
|
|
from django.contrib.contenttypes.fields import ContentType
|
|
from django.contrib.postgres.fields import JSONField
|
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from utils.models import (
|
|
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
|
|
URLImageMixin,
|
|
)
|
|
from utils.querysets import RelatedObjectsCountMixin
|
|
from utils.models import IntermediateGalleryModelMixin, GalleryModelMixin
|
|
|
|
|
|
# Mixins
|
|
class CollectionNameMixin(models.Model):
|
|
"""CollectionName mixin"""
|
|
name = models.CharField(_('name'), max_length=250)
|
|
|
|
class Meta:
|
|
"""Meta class"""
|
|
abstract = True
|
|
|
|
|
|
class CollectionDateMixin(models.Model):
|
|
"""CollectionDate mixin"""
|
|
start = models.DateTimeField(blank=True, null=True, default=None,
|
|
verbose_name=_('start'))
|
|
end = models.DateTimeField(blank=True, null=True, default=None,
|
|
verbose_name=_('end'))
|
|
|
|
class Meta:
|
|
"""Meta class"""
|
|
abstract = True
|
|
|
|
|
|
# Models
|
|
class CollectionQuerySet(RelatedObjectsCountMixin):
|
|
"""QuerySet for model Collection"""
|
|
|
|
def by_country_code(self, code):
|
|
"""Filter collection by country code."""
|
|
return self.filter(country__code=code)
|
|
|
|
def published(self):
|
|
"""Returned only published collection"""
|
|
return self.filter(is_publish=True)
|
|
|
|
|
|
class Collection(ProjectBaseMixin, CollectionDateMixin,
|
|
TranslatedFieldsMixin, URLImageMixin):
|
|
"""Collection model."""
|
|
STR_FIELD_NAME = 'name'
|
|
|
|
ORDINARY = 0 # Ordinary collection
|
|
POP = 1 # POP collection
|
|
|
|
COLLECTION_TYPES = (
|
|
(ORDINARY, _('Ordinary')),
|
|
(POP, _('Pop')),
|
|
)
|
|
|
|
name = TJSONField(verbose_name=_('name'),
|
|
help_text='{"en-GB":"some text"}')
|
|
collection_type = models.PositiveSmallIntegerField(choices=COLLECTION_TYPES,
|
|
default=ORDINARY,
|
|
verbose_name=_('Collection type'))
|
|
is_publish = models.BooleanField(
|
|
default=False, verbose_name=_('Publish status'))
|
|
on_top = models.BooleanField(
|
|
default=False, verbose_name=_('Position on top'))
|
|
country = models.ForeignKey(
|
|
'location.Country', verbose_name=_('country'), on_delete=models.CASCADE)
|
|
block_size = JSONField(
|
|
_('collection block properties'), null=True, blank=True,
|
|
default=None, help_text='{"width": "250px", "height":"250px"}')
|
|
description = TJSONField(
|
|
_('description'), null=True, blank=True,
|
|
default=None, help_text='{"en-GB":"some text"}')
|
|
slug = models.SlugField(max_length=50, unique=True,
|
|
verbose_name=_('Collection slug'), editable=True, null=True)
|
|
old_id = models.IntegerField(null=True, blank=True)
|
|
|
|
rank = models.IntegerField(null=True, default=None)
|
|
|
|
objects = CollectionQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('collection')
|
|
verbose_name_plural = _('collections')
|
|
|
|
@property
|
|
def _related_objects(self) -> list:
|
|
"""Return list of related objects."""
|
|
related_objects = []
|
|
# get related objects
|
|
for related_object in self._meta.related_objects:
|
|
related_objects.append(related_object)
|
|
return related_objects
|
|
|
|
@property
|
|
def count_related_objects(self) -> int:
|
|
"""Return count of related objects."""
|
|
counter = 0
|
|
# count of related objects
|
|
for related_object in [related_object.name for related_object in self._related_objects]:
|
|
counter += getattr(self, f'{related_object}').count()
|
|
return counter
|
|
|
|
@property
|
|
def related_object_names(self) -> list:
|
|
"""Return related object names."""
|
|
raw_objects = []
|
|
for related_object in [related_object.name for related_object in self._related_objects]:
|
|
instances = getattr(self, f'{related_object}')
|
|
if instances.exists():
|
|
for instance in instances.all():
|
|
raw_object = (instance.id, instance.establishment_type.index_name,
|
|
instance.slug) if \
|
|
hasattr(instance, 'slug') else (instance.id, None, None)
|
|
raw_objects.append(raw_object)
|
|
|
|
# parse slugs
|
|
related_objects = []
|
|
object_names = set()
|
|
re_pattern = r'[\w]+'
|
|
for object_id, object_type, raw_name, in raw_objects:
|
|
result = re.findall(re_pattern, raw_name)
|
|
if result:
|
|
name = ' '.join(result).capitalize()
|
|
if name not in object_names:
|
|
related_objects.append({
|
|
'id': object_id,
|
|
'establishment_type': object_type,
|
|
'name': name
|
|
})
|
|
object_names.add(name)
|
|
|
|
return related_objects
|
|
|
|
|
|
class GuideTypeQuerySet(models.QuerySet):
|
|
"""QuerySet for model GuideType."""
|
|
|
|
|
|
class GuideType(ProjectBaseMixin):
|
|
"""GuideType model."""
|
|
|
|
name = models.SlugField(max_length=255, unique=True,
|
|
verbose_name=_('code'))
|
|
|
|
objects = GuideTypeQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide type')
|
|
verbose_name_plural = _('guide types')
|
|
|
|
def __str__(self):
|
|
"""Overridden str dunder method."""
|
|
return self.name
|
|
|
|
|
|
class GuideQuerySet(models.QuerySet):
|
|
"""QuerySet for Guide."""
|
|
|
|
|
|
class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
|
"""Guide model."""
|
|
BUILT = 0
|
|
WAITING = 1
|
|
REMOVING = 2
|
|
BUILDING = 3
|
|
|
|
STATE_CHOICES = (
|
|
(BUILT, 'built'),
|
|
(WAITING, 'waiting'),
|
|
(REMOVING, 'removing'),
|
|
(BUILDING, 'building'),
|
|
|
|
)
|
|
|
|
start = models.DateTimeField(null=True,
|
|
verbose_name=_('start'))
|
|
vintage = models.IntegerField(validators=[MinValueValidator(1900),
|
|
MaxValueValidator(2100)],
|
|
null=True,
|
|
verbose_name=_('guide vintage year'))
|
|
slug = models.SlugField(max_length=255, unique=True, null=True,
|
|
verbose_name=_('slug'))
|
|
guide_type = models.ForeignKey('GuideType', on_delete=models.PROTECT,
|
|
null=True,
|
|
verbose_name=_('type'))
|
|
site = models.ForeignKey('main.SiteSettings', on_delete=models.SET_NULL,
|
|
null=True,
|
|
verbose_name=_('site settings'))
|
|
state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES,
|
|
verbose_name=_('state'))
|
|
old_id = models.IntegerField(blank=True, null=True)
|
|
|
|
objects = GuideQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide')
|
|
verbose_name_plural = _('guides')
|
|
|
|
def __str__(self):
|
|
"""String method."""
|
|
return f'{self.name}'
|
|
|
|
@property
|
|
def entities(self):
|
|
"""Return entities and its count."""
|
|
# todo: to work
|
|
return {
|
|
'Current': 0,
|
|
'Initial': 0,
|
|
'Restaurants': 0,
|
|
'Shops': 0,
|
|
}
|
|
|
|
|
|
class AdvertorialQuerySet(models.QuerySet):
|
|
"""QuerySet for model Advertorial."""
|
|
|
|
|
|
class Advertorial(ProjectBaseMixin):
|
|
"""Guide advertorial model."""
|
|
number_of_pages = models.PositiveIntegerField(
|
|
verbose_name=_('number of pages'),
|
|
help_text=_('the total number of reserved pages'))
|
|
right_pages = models.PositiveIntegerField(
|
|
verbose_name=_('number of right pages'),
|
|
help_text=_('the number of right pages (which are part of total number).'))
|
|
guide_element = models.OneToOneField('GuideElement', on_delete=models.CASCADE,
|
|
related_name='advertorial',
|
|
verbose_name=_('guide element'))
|
|
old_id = models.IntegerField(blank=True, null=True)
|
|
|
|
objects = AdvertorialQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('advertorial')
|
|
verbose_name_plural = _('advertorials')
|
|
|
|
|
|
class GuideFilterQuerySet(models.QuerySet):
|
|
"""QuerySet for model GuideFilter."""
|
|
|
|
|
|
class GuideFilter(ProjectBaseMixin):
|
|
"""Guide filter model."""
|
|
establishment_type_json = JSONField(blank=True, null=True,
|
|
verbose_name='establishment types')
|
|
country_json = JSONField(blank=True, null=True,
|
|
verbose_name='countries')
|
|
region_json = JSONField(blank=True, null=True,
|
|
verbose_name='regions')
|
|
sub_region_json = JSONField(blank=True, null=True,
|
|
verbose_name='sub regions')
|
|
wine_region_json = JSONField(blank=True, null=True,
|
|
verbose_name='wine regions')
|
|
with_mark = models.BooleanField(default=True,
|
|
verbose_name=_('with mark'),
|
|
help_text=_('exclude empty marks?'))
|
|
locale_json = JSONField(blank=True, null=True,
|
|
verbose_name='locales')
|
|
max_mark = models.FloatField(verbose_name=_('max mark'),
|
|
null=True,
|
|
help_text=_('mark under'))
|
|
min_mark = models.FloatField(verbose_name=_('min mark'),
|
|
null=True,
|
|
help_text=_('mark over'))
|
|
review_vintage_json = JSONField(verbose_name='review vintage years')
|
|
review_state_json = JSONField(blank=True, null=True,
|
|
verbose_name='review states')
|
|
guide = models.OneToOneField(Guide, on_delete=models.CASCADE,
|
|
verbose_name=_('guide'))
|
|
old_id = models.IntegerField(blank=True, null=True)
|
|
|
|
objects = GuideFilterQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide filter')
|
|
verbose_name_plural = _('guide filters')
|
|
|
|
|
|
class GuideElementType(models.Model):
|
|
"""Model for type of guide elements."""
|
|
|
|
name = models.CharField(max_length=50,
|
|
verbose_name=_('name'))
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide element type')
|
|
verbose_name_plural = _('guide element types')
|
|
|
|
def __str__(self):
|
|
"""Overridden str dunder."""
|
|
return self.name
|
|
|
|
|
|
class GuideWineColorSectionQuerySet(models.QuerySet):
|
|
"""QuerySet for model GuideWineColorSection."""
|
|
|
|
|
|
class GuideWineColorSection(ProjectBaseMixin):
|
|
"""Sections for wine colors."""
|
|
|
|
name = models.CharField(max_length=255, verbose_name=_('section name'))
|
|
|
|
objects = GuideWineColorSectionQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide wine color section')
|
|
verbose_name_plural = _('guide wine color sections')
|
|
|
|
|
|
class GuideElementSectionCategoryQuerySet(models.QuerySet):
|
|
"""QuerySet for model GuideElementSectionCategory."""
|
|
|
|
|
|
class GuideElementSectionCategory(ProjectBaseMixin):
|
|
"""Section category for guide element."""
|
|
|
|
name = models.CharField(max_length=255,
|
|
verbose_name=_('category name'))
|
|
|
|
objects = GuideElementSectionCategoryQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide element section category')
|
|
verbose_name_plural = _('guide element section categories')
|
|
|
|
|
|
class GuideElementSectionQuerySet(models.QuerySet):
|
|
"""QuerySet for model GuideElementSection."""
|
|
|
|
|
|
class GuideElementSection(ProjectBaseMixin):
|
|
"""Sections for guide element."""
|
|
|
|
name = models.CharField(max_length=255, verbose_name=_('section name'))
|
|
category = models.ForeignKey(GuideElementSectionCategory, on_delete=models.PROTECT,
|
|
verbose_name=_('category'))
|
|
old_id = models.PositiveIntegerField(blank=True, null=True, default=None,
|
|
verbose_name=_('old id'))
|
|
|
|
objects = GuideElementSectionQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide element section')
|
|
verbose_name_plural = _('guide element sections')
|
|
|
|
|
|
class GuideElementQuerySet(models.QuerySet):
|
|
"""QuerySet for model Guide elements."""
|
|
|
|
|
|
class GuideElement(ProjectBaseMixin, MPTTModel):
|
|
"""Frozen state of elements of guide instance."""
|
|
|
|
guide_element_type = models.ForeignKey('GuideElementType', on_delete=models.SET_NULL,
|
|
null=True,
|
|
verbose_name=_('guide element type'))
|
|
establishment = models.ForeignKey('establishment.Establishment', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
review = models.ForeignKey('review.Review', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
product = models.ForeignKey('product.Product', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
priority = models.IntegerField(null=True, blank=True, default=None)
|
|
city = models.ForeignKey('location.City', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
wine_color_section = models.ForeignKey('GuideWineColorSection', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
section = models.ForeignKey('GuideElementSection', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
guide = models.ForeignKey('Guide', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None)
|
|
parent = TreeForeignKey('self', on_delete=models.CASCADE,
|
|
null=True, blank=True,
|
|
related_name='children')
|
|
label_photo = models.ForeignKey('gallery.Image', on_delete=models.SET_NULL,
|
|
null=True, blank=True, default=None,
|
|
verbose_name=_('label photo'))
|
|
old_id = models.PositiveIntegerField(blank=True, null=True, default=None,
|
|
verbose_name=_('old id'))
|
|
|
|
objects = GuideElementQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
"""Meta class."""
|
|
verbose_name = _('guide element')
|
|
verbose_name_plural = _('guide elements')
|
|
|
|
class MPTTMeta:
|
|
order_insertion_by = ['guide_element_type']
|
|
|
|
def __str__(self):
|
|
"""Overridden dunder method."""
|
|
return self.guide_element_type.name if self.guide_element_type else self.id
|