added tags

This commit is contained in:
Anatoly 2019-11-07 19:03:12 +03:00
parent 0aa0378ae0
commit 9bdeabe352
7 changed files with 334 additions and 108 deletions

View File

@ -1,19 +1,12 @@
"""Product admin conf."""
from django.contrib import admin
from utils.admin import BaseModelAdminMixin
from .models import Product, ProductType, ProductSubType, Unit, \
Characteristic, ProductCharacteristic
class ProductCharacteristics(admin.TabularInline):
model = ProductCharacteristic
extra = 0
from .models import Product, ProductType, ProductSubType, Unit
@admin.register(Product)
class ProductAdmin(BaseModelAdminMixin, admin.ModelAdmin):
"""Admin page for model Product."""
inlines = [ProductCharacteristics, ]
@admin.register(ProductType)
@ -29,8 +22,3 @@ class ProductSubTypeAdmin(admin.ModelAdmin):
@admin.register(Unit)
class UnitAdmin(admin.ModelAdmin):
"""Admin page for model Unit."""
@admin.register(Characteristic)
class CharacteristicAdmin(admin.ModelAdmin):
"""Admin page for model Characteristic."""

View File

@ -1,10 +1,10 @@
"""Product app models."""
from django.db import models
from django.contrib.gis.db import models as gis_models
from django.contrib.contenttypes import fields as generic
from django.contrib.gis.db import models as gis_models
from django.core.exceptions import ValidationError
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.utils.translation import gettext_lazy as _
from utils.models import (BaseAttributes, ProjectBaseMixin,
TranslatedFieldsMixin, TJSONField)
@ -31,6 +31,9 @@ class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
unique=True, db_index=True,
verbose_name=_('Index name'))
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='product_types',
verbose_name=_('Tag'))
class Meta:
"""Meta class."""
@ -120,6 +123,27 @@ class ProductQuerySet(models.QuerySet):
return self.filter(subtypes__index_name=product_subtype)
class ProductBrandQuerySet(models.QuerySet):
"""QuerySet for model ProductBrand."""
class ProductBrand(models.Model):
name = models.CharField(max_length=255, unique=True,
verbose_name=_('product brand'))
objects = ProductBrandQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('Brand')
verbose_name_plural = _('Brands')
def __str__(self):
"""Overridden str dunder."""
return self.name
class Product(TranslatedFieldsMixin, BaseAttributes):
"""Product models."""
@ -131,6 +155,16 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
(ONLINE, _('Online')),
)
PUBLISHED = 0
OUT_OF_PRODUCTION = 1
WAITING = 2
STATE_CHOICES = (
(PUBLISHED, _('Published')),
(OUT_OF_PRODUCTION, _('Out_of_production')),
(WAITING, _('Waiting')),
)
category = models.PositiveIntegerField(choices=CATEGORY_CHOICES,
default=COMMON)
name = models.CharField(max_length=255,
@ -138,9 +172,6 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
verbose_name=_('name'))
description = TJSONField(_('Description'), null=True, blank=True,
default=None, help_text='{"en-GB":"some text"}')
characteristics = models.ManyToManyField('Characteristic',
through='ProductCharacteristic',
verbose_name=_('characteristics'))
country = models.ManyToManyField('location.Country',
verbose_name=_('Country'))
available = models.BooleanField(_('Available'), default=True)
@ -170,6 +201,14 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
favorites = generic.GenericRelation(to='favorites.Favorites')
old_id = models.PositiveIntegerField(_('old id'), default=None,
blank=True, null=True)
state = models.PositiveIntegerField(choices=STATE_CHOICES,
default=WAITING,
verbose_name=_('state'),
help_text=_('attribute from legacy db'))
brand = models.ForeignKey(ProductBrand, on_delete=models.PROTECT, null=True,
verbose_name=_('brand'))
tags = models.ManyToManyField('tag.Tag', related_name='products',
verbose_name=_('Tag'))
objects = ProductManager.from_queryset(ProductQuerySet)()
@ -232,39 +271,6 @@ class Unit(models.Model):
return self.name
class Characteristic(models.Model):
"""Characteristic model."""
name = models.CharField(_('name'), max_length=255)
value = models.CharField(max_length=255, null=True, blank=True,
verbose_name=_('value'))
# unit = models.ForeignKey('Unit', on_delete=models.PROTECT)
priority = models.IntegerField(unique=True, null=True, default=None)
old_id = models.PositiveIntegerField(blank=True, null=True, default=None)
class Meta:
"""Meta model."""
verbose_name = _('characteristic')
verbose_name_plural = _('characteristics')
def __str__(self):
return self.name
class ProductCharacteristic(models.Model):
product = models.ForeignKey('Product', on_delete=models.CASCADE)
characteristic = models.ForeignKey('Characteristic', on_delete=models.CASCADE)
class Meta:
"""Meta class."""
verbose_name = _('product characteristic')
verbose_name_plural = _('product characteristics')
def __str__(self):
return f'{self.characteristic.name} - {self.characteristic.value}'
class WineStandardQuerySet(models.QuerySet):
"""Wine appellation queryset."""
@ -308,8 +314,8 @@ class WineClassification(models.Model):
name = models.CharField(_('name'), max_length=255)
standard = models.ForeignKey(WineStandard, on_delete=models.PROTECT,
verbose_name=_('standard'))
possible_color = models.ForeignKey(Characteristic, on_delete=models.PROTECT,
help_text=_('Legacy attribute'))
tags = models.ManyToManyField('tag.Tag', related_name='wine_classifications',
verbose_name=_('Tag'))
possible_subtype = models.ForeignKey(ProductSubType, on_delete=models.PROTECT,
blank=True, null=True, default=None,
help_text=_('Legacy attribute - possible_type'))

View File

@ -1,5 +1,7 @@
from pprint import pprint
from django.conf import settings
from tag.models import TagCategory
from transfer import models as transfer_models
from transfer.serializers.partner import PartnerSerializer
from transfer.serializers import product as product_serializers
@ -16,6 +18,16 @@ def transfer_partner():
def transfer_wine_color():
wine_color_index_name = 'wine_color'
TagCategory.objects.get_or_create(
index_name=wine_color_index_name,
defaults={
'label': {settings.FALLBACK_LOCALE: wine_color_index_name},
'value_type': TagCategory.STRING,
'index_name': wine_color_index_name,
'public': True
})
queryset = transfer_models.WineColor.objects.all()
serialized_data = product_serializers.WineColorSerializer(
data=list(queryset.values()),
@ -26,6 +38,37 @@ def transfer_wine_color():
pprint(f"WineColorSerializer errors: {serialized_data.errors}")
def transfer_wine_bottles_produced():
bottles_produced_index_name = 'bottles_produced'
TagCategory.objects.get_or_create(
index_name=bottles_produced_index_name,
defaults={
'label': {settings.FALLBACK_LOCALE: bottles_produced_index_name},
'value_type': TagCategory.STRING,
'index_name': bottles_produced_index_name,
'public': True
})
raw_queryset = transfer_models.Products.objects.raw(
"""
SELECT
DISTINCT bottles_produced,
1 as id
FROM products
WHERE bottles_produced IS NOT NULL
AND bottles_produced !='';
"""
)
queryset = [vars(query) for query in raw_queryset]
serialized_data = product_serializers.BottlesProducedSerializer(
data=queryset,
many=True)
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f"transfer_wine_bottles_produced errors: {serialized_data.errors}")
def transfer_wine_standard():
queryset = transfer_models.WineClassification.objects.filter(parent_id__isnull=True)
serialized_data = product_serializers.WineStandardSerializer(
@ -34,7 +77,7 @@ def transfer_wine_standard():
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f"WineStandardSerializer errors: {serialized_data.errors}")
pprint(f"transfer_wine_standard errors: {serialized_data.errors}")
def transfer_wine_classifications():
@ -45,12 +88,50 @@ def transfer_wine_classifications():
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f"WineStandardClassificationSerializer errors: {serialized_data.errors}")
pprint(f"transfer_wine_classifications errors: {serialized_data.errors}")
def transfer_product_brand():
raw_queryset = transfer_models.Products.objects.raw(
"""
SELECT
DISTINCT brand,
1 as id
FROM products
WHERE brand IS NOT NULL
AND brand !='';
"""
)
queryset = [vars(query) for query in raw_queryset]
serialized_data = product_serializers.ProductBrandSerializer(
data=queryset,
many=True)
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f"transfer_product_brand errors: {serialized_data.errors}")
def transfer_product():
queryset = transfer_models.Products.objects.all()
serialized_data = product_serializers.ProductSerializer(
data=list(queryset.values()),
many=True)
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f"transfer_product errors: {serialized_data.errors}")
data_types = {
"partner": [transfer_partner],
"wine_color": [transfer_wine_color],
"wine_standard": [transfer_wine_standard],
"wine_classification": [transfer_wine_classifications],
"wine_characteristics": [
transfer_wine_color,
transfer_wine_bottles_produced,
transfer_wine_standard,
transfer_wine_classifications],
"product": [
transfer_product_brand,
transfer_product
],
}

View File

@ -34,8 +34,9 @@ class Tag(TranslatedFieldsMixin, models.Model):
category = models.ForeignKey('TagCategory', on_delete=models.CASCADE,
null=True, related_name='tags',
verbose_name=_('Category'))
chosen_tag_settings = models.ManyToManyField(Country, through='ChosenTagSettings')
priority = models.PositiveIntegerField(null=True, default=0)
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
objects = TagQuerySet.as_manager()

View File

@ -27,9 +27,8 @@ class Command(BaseCommand):
LONG_DATA_TYPES = [
'update_country_flag',
'wine_color',
'wine_standard',
'wine_classification',
'wine_characteristics',
'product',
]
def handle(self, *args, **options):

View File

@ -937,36 +937,36 @@ class WineClassification(MigrateMixin):
db_table = 'wine_classifications'
# class Products(models.Model):
# establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True)
# brand = models.CharField(max_length=255, blank=True, null=True)
# name = models.CharField(max_length=255, blank=True, null=True)
# vintage = models.CharField(max_length=255, blank=True, null=True)
# type = models.CharField(max_length=255, blank=True, null=True)
# unique_key = models.CharField(max_length=255, blank=True, null=True)
# price = models.FloatField(blank=True, null=True)
# average_price_in_shops = models.FloatField(blank=True, null=True)
# fra_encima_id = models.IntegerField(blank=True, null=True)
# wine_sub_region_id = models.IntegerField(blank=True, null=True)
# classification = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
# wine_region = models.ForeignKey('WineLocations', models.DO_NOTHING, blank=True, null=True)
# wine_type = models.ForeignKey('WineTypes', models.DO_NOTHING, blank=True, null=True)
# wine_color = models.ForeignKey('WineColors', models.DO_NOTHING, blank=True, null=True)
# appellation = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
# created_at = models.DateTimeField()
# updated_at = models.DateTimeField()
# state = models.CharField(max_length=255, blank=True, null=True)
# wine_style = models.ForeignKey('WineStyles', models.DO_NOTHING, blank=True, null=True)
# village = models.ForeignKey('WineLocations', models.DO_NOTHING, blank=True, null=True)
# vineyard = models.ForeignKey('WineLocations', models.DO_NOTHING, blank=True, null=True)
# yard_classification = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
# wine_quality = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
# bottles_produced = models.CharField(max_length=3000, blank=True, null=True)
# deu_import_id = models.IntegerField(blank=True, null=True)
#
# class Meta:
# managed = False
# db_table = 'products'
class Products(MigrateMixin):
using = 'legacy'
establishment = models.ForeignKey('Establishments', models.DO_NOTHING, null=True)
brand = models.CharField(max_length=255, null=True)
name = models.CharField(max_length=255, null=True)
vintage = models.CharField(max_length=255, null=True)
type = models.CharField(max_length=255, null=True)
price = models.FloatField(null=True)
average_price_in_shops = models.FloatField(null=True)
wine_sub_region_id = models.IntegerField(null=True)
classification = models.ForeignKey('WineClassification', models.DO_NOTHING, null=True,
related_name='product_classification')
wine_region = models.ForeignKey('WineLocations', models.DO_NOTHING, null=True)
wine_type = models.ForeignKey('WineType', models.DO_NOTHING, null=True)
wine_color = models.ForeignKey('WineColor', models.DO_NOTHING, null=True)
appellation = models.ForeignKey('WineClassification', models.DO_NOTHING, null=True)
state = models.CharField(max_length=255)
village = models.ForeignKey('WineLocations', models.DO_NOTHING, null=True,
related_name='product_village')
vineyard = models.ForeignKey('WineLocations', models.DO_NOTHING, null=True,
related_name='product_vineyard')
wine_quality = models.ForeignKey('WineClassification', models.DO_NOTHING, null=True,
related_name='product_wine_quality')
bottles_produced = models.CharField(max_length=3000, null=True)
class Meta:
managed = False
db_table = 'products'
class HomePages(models.Model):
using = 'legacy'

View File

@ -1,36 +1,71 @@
from rest_framework import serializers
from django.utils.text import slugify
from product import models
from transfer.models import WineClassification
from location import models as location_models
from tag import models as tag_models
from establishment import models as establishment_models
from transfer import models as transfer_models
from utils.methods import get_point_from_coordinates
from transfer.mixins import TransferSerializerMixin
from django.conf import settings
class WineColorSerializer(TransferSerializerMixin):
NAME = 'wine_color'
TAG_CATEGORY = 'wine_color'
id = serializers.IntegerField()
name = serializers.CharField(allow_null=True)
order_number = serializers.IntegerField(allow_null=True)
class Meta:
model = models.Characteristic
model = tag_models.Tag
fields = (
'id',
'name',
'value',
'priority',
'order_number',
)
def validate(self, attrs):
attrs['old_id'] = attrs.pop('id')
attrs['value'] = attrs['name']
attrs['name'] = self.NAME
value = attrs.pop('name')
attrs['old_id'] = attrs.pop('id', None)
attrs['label'] = {settings.FALLBACK_LOCALE: value}
attrs['value'] = slugify(value)
attrs['priority'] = attrs.pop('order_number')
attrs['category'] = self.tag_category
return attrs
@property
def tag_category(self):
qs = tag_models.TagCategory.objects.filter(index_name=self.TAG_CATEGORY)
if qs.exists():
return qs.first()
class BottlesProducedSerializer(TransferSerializerMixin):
TAG_CATEGORY = 'bottles_produced'
bottles_produced = serializers.IntegerField()
class Meta:
model = tag_models.Tag
fields = (
'bottles_produced',
)
def validate(self, attrs):
value = attrs.pop('bottles_produced')
attrs['label'] = {settings.FALLBACK_LOCALE: value}
attrs['value'] = slugify(value)
attrs['category'] = self.tag_category
return attrs
@property
def tag_category(self):
qs = tag_models.TagCategory.objects.filter(index_name=self.TAG_CATEGORY)
if qs.exists():
return qs.first()
class WineStandardSerializer(TransferSerializerMixin):
id = serializers.IntegerField()
@ -60,7 +95,7 @@ class WineStandardSerializer(TransferSerializerMixin):
return attrs
class WineStandardClassificationSerializer(TransferSerializerMixin):
class WineStandardClassificationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField()
@ -84,20 +119,28 @@ class WineStandardClassificationSerializer(TransferSerializerMixin):
parent_id = attrs.pop('parent_id', None)
attrs['old_id'] = attrs.pop('id')
attrs['possible_subtype'] = self.get_possible_type(possible_type_id)
attrs['possible_color'] = self.get_possible_color(possible_color_id)
attrs['possible_color_tag'] = self.get_possible_color_tag(possible_color_id)
attrs['standard'] = self.get_wine_standard(parent_id)
return attrs
def create(self, validated_data):
possible_color_tag = validated_data.pop('possible_color_tag', None)
obj = super().create(validated_data)
if possible_color_tag:
obj.tags.add(possible_color_tag)
return obj
def get_possible_type(self, possible_type_id):
legacy_qs = WineClassification.objects.filter(id=possible_type_id)
legacy_qs = transfer_models.WineClassification.objects.filter(id=possible_type_id)
if legacy_qs.exists():
qs = models.ProductSubType.objects.filter(index_name=legacy_qs.first())
if qs.exists():
return qs.first()
def get_possible_color(self, possible_color_id):
qs = models.Characteristic.objects.filter(name=WineColorSerializer.NAME,
old_id=possible_color_id)
def get_possible_color_tag(self, possible_color_id):
qs = tag_models.Tag.objects.filter(
old_id=possible_color_id,
category__index_name=slugify(WineColorSerializer.TAG_CATEGORY))
if qs.exists():
return qs.first()
@ -105,3 +148,111 @@ class WineStandardClassificationSerializer(TransferSerializerMixin):
qs = models.WineStandard.objects.filter(old_id=parent_id)
if qs.exists():
return qs.first()
class ProductBrandSerializer(TransferSerializerMixin):
brand = serializers.CharField()
class Meta:
model = models.ProductBrand
fields = (
'brand',
)
def validate(self, attrs):
attrs['name'] = attrs.pop('brand')
return attrs
class ProductSerializer(TransferSerializerMixin):
establishment_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.Establishments.objects.all(),
allow_null=True)
classification_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineClassification.objects.all(),
allow_null=True)
wine_region_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineLocations.objects.all(),
allow_null=True)
wine_type_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineType.objects.all(),
allow_null=True)
wine_color_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineColor.objects.all(),
allow_null=True)
appellation_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineClassification.objects.all(),
allow_null=True)
village_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineLocations.objects.all(),
allow_null=True)
vineyard_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineLocations.objects.all(),
allow_null=True)
wine_quality_id = serializers.PrimaryKeyRelatedField(
queryset=transfer_models.WineClassification.objects.all(),
allow_null=True)
brand = serializers.CharField(allow_null=True)
name = serializers.CharField(allow_null=True)
vintage = serializers.CharField(allow_null=True)
type = serializers.CharField(allow_null=True)
price = serializers.CharField(allow_null=True)
average_price_in_shops = serializers.CharField(allow_null=True)
wine_sub_region_id = serializers.CharField(allow_null=True)
state = serializers.CharField()
bottles_produced = serializers.CharField(allow_null=True)
class Meta:
model = models.Product
fields = (
'establishment_id',
'classification_id',
'wine_region_id',
'wine_type_id',
'wine_color_id',
'appellation_id',
'village_id',
'vineyard_id',
'wine_quality_id',
'brand',
'name',
'vintage',
'type',
'price',
'average_price_in_shops',
'wine_sub_region_id',
'state',
'bottles_produced',
)
def validate(self, attrs):
establishment_old_id = attrs.pop('establishment_id', None)
state = attrs.pop('state', None)
brand = attrs.pop('brand', None)
attrs['establishment'] = self.get_establishment(establishment_old_id)
attrs['state'] = self.get_state(state)
attrs['brand'] = self.get_brand(brand)
import ipdb; ipdb.set_trace()
return attrs
def get_establishment(self, establishment_old_id):
qs = establishment_models.Establishment.objects.filter(
old_id=establishment_old_id)
if qs.exists():
return qs.first()
def get_state(self, state):
if state == 'published':
return models.Product.PUBLISHED
elif state == 'out_of_production':
return models.Product.OUT_OF_PRODUCTION
return models.Product.WAITING
def get_brand(self, brand):
qs = models.ProductBrand.objects.filter(brand__icontains=brand)
if qs.exists():
return qs.first()