Merge remote-tracking branch 'origin/develop' into feature/migrate-inquiries
# Conflicts: # apps/transfer/management/commands/transfer.py # apps/transfer/models.py
This commit is contained in:
commit
7106249d1e
|
|
@ -1,6 +1,7 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
from django.db.models import Q
|
from django.db.models import Q, F, Value
|
||||||
|
from django.db.models.functions import ConcatPair
|
||||||
from establishment.management.commands.add_position import namedtuplefetchall
|
from establishment.management.commands.add_position import namedtuplefetchall
|
||||||
from account.models import User
|
from account.models import User
|
||||||
|
|
||||||
|
|
@ -11,33 +12,29 @@ class Command(BaseCommand):
|
||||||
def account_sql(self):
|
def account_sql(self):
|
||||||
with connections['legacy'].cursor() as cursor:
|
with connections['legacy'].cursor() as cursor:
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
select a.email, a.id as account_id, a.encrypted_password
|
select a.email, a.id as account_id, a.encrypted_password,
|
||||||
|
case when a.confirmed_at is not null then true else false end as confirmed_at,
|
||||||
|
case when a.confirmed_at is null then true else false end as unconfirmed_email,
|
||||||
|
nickname
|
||||||
from accounts as a
|
from accounts as a
|
||||||
where a.email is not null
|
where a.email is not null and a.nickname!='admin'
|
||||||
and a.email not in ('cyril@tomatic.net',
|
|
||||||
'cyril2@tomatic.net',
|
|
||||||
'd.sadykova@id-east.ru',
|
|
||||||
'd.sadykova@octopod.ru',
|
|
||||||
'n.yurchenko@id-east.ru'
|
|
||||||
)
|
|
||||||
and a.confirmed_at is not null
|
|
||||||
''')
|
''')
|
||||||
return namedtuplefetchall(cursor)
|
return namedtuplefetchall(cursor)
|
||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
objects = []
|
objects = []
|
||||||
for a in self.account_sql():
|
for a in self.account_sql():
|
||||||
count = User.objects.filter(Q(email=a.email) | Q(old_id=a.account_id)).count()
|
users = User.objects.filter(Q(email=a.email) | Q(old_id=a.account_id))
|
||||||
if count == 0:
|
if not users.exists():
|
||||||
objects.append(User(email=a.email,
|
objects.append(User(email=a.email,
|
||||||
unconfirmed_email=False,
|
unconfirmed_email=a.unconfirmed_email,
|
||||||
email_confirmed=True,
|
email_confirmed=a.confirmed_at,
|
||||||
old_id=a.account_id,
|
old_id=a.account_id,
|
||||||
password='bcrypt$'+a.encrypted_password
|
password=a.encrypted_password,
|
||||||
|
username=a.nickname
|
||||||
))
|
))
|
||||||
else:
|
|
||||||
user = User.objects.filter(Q(email=a.email) | Q(old_id=a.account_id))
|
|
||||||
user.update(password='bcrypt$'+a.encrypted_password)
|
|
||||||
|
|
||||||
User.objects.bulk_create(objects)
|
User.objects.bulk_create(objects)
|
||||||
|
user = User.objects.filter(old_id__isnull=False)
|
||||||
|
user.update(password=ConcatPair(Value('bcrypt$'), F('password')))
|
||||||
self.stdout.write(self.style.WARNING(f'Created accounts objects.'))
|
self.stdout.write(self.style.WARNING(f'Created accounts objects.'))
|
||||||
|
|
@ -21,13 +21,6 @@ class Command(BaseCommand):
|
||||||
select a.email, a.id as account_id
|
select a.email, a.id as account_id
|
||||||
from accounts as a
|
from accounts as a
|
||||||
where a.email is not null
|
where a.email is not null
|
||||||
and a.email not in ('cyril@tomatic.net',
|
|
||||||
'cyril2@tomatic.net',
|
|
||||||
'd.sadykova@id-east.ru',
|
|
||||||
'd.sadykova@octopod.ru',
|
|
||||||
'n.yurchenko@id-east.ru'
|
|
||||||
)
|
|
||||||
and a.confirmed_at is not null
|
|
||||||
) t
|
) t
|
||||||
join identities i on i.account_id = t.account_id
|
join identities i on i.account_id = t.account_id
|
||||||
''')
|
''')
|
||||||
|
|
@ -39,13 +32,12 @@ class Command(BaseCommand):
|
||||||
user = User.objects.filter(old_id=s.account_id)
|
user = User.objects.filter(old_id=s.account_id)
|
||||||
if user.count() > 0:
|
if user.count() > 0:
|
||||||
social = UserSocialAuth.objects.filter(user=user.first(),
|
social = UserSocialAuth.objects.filter(user=user.first(),
|
||||||
provider=s.provider,
|
provider=s.provider,
|
||||||
uid=s.uid)
|
uid=s.uid)
|
||||||
if social.count() == 0:
|
if social.count() == 0:
|
||||||
objects.append(UserSocialAuth(user=user.first(), provider=s.provider,
|
objects.append(UserSocialAuth(user=user.first(), provider=s.provider,
|
||||||
uid=s.uid)
|
uid=s.uid)
|
||||||
)
|
)
|
||||||
print('INSERT')
|
|
||||||
|
|
||||||
UserSocialAuth.objects.bulk_create(objects)
|
UserSocialAuth.objects.bulk_create(objects)
|
||||||
self.stdout.write(self.style.WARNING(f'Created social objects.'))
|
self.stdout.write(self.style.WARNING(f'Created social objects.'))
|
||||||
|
|
@ -7,11 +7,11 @@ from transfer.serializers.account import UserSerializer
|
||||||
from transfer.serializers.user_social_auth import UserSocialAuthSerializer
|
from transfer.serializers.user_social_auth import UserSocialAuthSerializer
|
||||||
|
|
||||||
STOP_LIST = (
|
STOP_LIST = (
|
||||||
'cyril@tomatic.net',
|
# 'cyril@tomatic.net',
|
||||||
'cyril2@tomatic.net',
|
# 'cyril2@tomatic.net',
|
||||||
'd.sadykova@id-east.ru',
|
# 'd.sadykova@id-east.ru',
|
||||||
'd.sadykova@octopod.ru',
|
# 'd.sadykova@octopod.ru',
|
||||||
'n.yurchenko@id-east.ru',
|
# 'n.yurchenko@id-east.ru',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ def transfer_user():
|
||||||
# queryset = queryset.annotate(nickname=F('account__nickname'))
|
# queryset = queryset.annotate(nickname=F('account__nickname'))
|
||||||
# queryset = queryset.annotate(email=F('account__email'))
|
# queryset = queryset.annotate(email=F('account__email'))
|
||||||
|
|
||||||
queryset = Accounts.objects.filter(confirmed_at__isnull=False).exclude(email__in=STOP_LIST)
|
queryset = Accounts.objects.exclude(email__in=STOP_LIST)
|
||||||
|
|
||||||
serialized_data = UserSerializer(data=list(queryset.values()), many=True)
|
serialized_data = UserSerializer(data=list(queryset.values()), many=True)
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ def transfer_user():
|
||||||
def transfer_identities():
|
def transfer_identities():
|
||||||
queryset = Identities.objects.exclude(
|
queryset = Identities.objects.exclude(
|
||||||
Q(account_id__isnull=True) |
|
Q(account_id__isnull=True) |
|
||||||
Q(account__confirmed_at__isnull=True) |
|
# Q(account__confirmed_at__isnull=True) |
|
||||||
Q(account__email__in=STOP_LIST)
|
Q(account__email__in=STOP_LIST)
|
||||||
).values_list(
|
).values_list(
|
||||||
'account_id',
|
'account_id',
|
||||||
|
|
|
||||||
9
apps/collection/serializers/back.py
Normal file
9
apps/collection/serializers/back.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from collection import models
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionSerializer(serializers.ModelSerializer):
|
||||||
|
"""Collection serializer."""
|
||||||
|
class Meta:
|
||||||
|
model = models.Collection.objects.all()
|
||||||
|
fields = '__all__'
|
||||||
11
apps/collection/urls/back.py
Normal file
11
apps/collection/urls/back.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
"""Collection common urlpaths."""
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from collection.views import back as views
|
||||||
|
|
||||||
|
app_name = 'collection'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.CollectionListCreateView.as_view(), name='list-create'),
|
||||||
|
|
||||||
|
]
|
||||||
19
apps/collection/views/back.py
Normal file
19
apps/collection/views/back.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
from rest_framework import generics, permissions
|
||||||
|
from collection import models
|
||||||
|
from collection.serializers import common, back
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionListCreateView(generics.ListCreateAPIView):
|
||||||
|
"""Collection list-create view."""
|
||||||
|
queryset = models.Collection.objects.all()
|
||||||
|
serializer_class = back.CollectionSerializer
|
||||||
|
# todo: conf. permissions by TT
|
||||||
|
permission_classes = (permissions.IsAuthenticated, )
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
"""Collection list-create view."""
|
||||||
|
queryset = models.Collection.objects.all()
|
||||||
|
serializer_class = back.CollectionSerializer
|
||||||
|
# todo: conf. permissions by TT
|
||||||
|
permission_classes = (permissions.IsAuthenticated, )
|
||||||
|
|
@ -16,7 +16,6 @@ def transfer_establishment():
|
||||||
old_establishments = Establishments.objects.exclude(
|
old_establishments = Establishments.objects.exclude(
|
||||||
id__in=list(Establishment.objects.all().values_list('old_id', flat=True))
|
id__in=list(Establishment.objects.all().values_list('old_id', flat=True))
|
||||||
).exclude(
|
).exclude(
|
||||||
Q(type='Wineyard') |
|
|
||||||
Q(location__timezone__isnull=True),
|
Q(location__timezone__isnull=True),
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
'establishmentinfos_set',
|
'establishmentinfos_set',
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,6 @@ class CityAdmin(admin.ModelAdmin):
|
||||||
"""City admin."""
|
"""City admin."""
|
||||||
|
|
||||||
|
|
||||||
class WineAppellationInline(admin.TabularInline):
|
|
||||||
model = models.WineAppellation
|
|
||||||
extra = 0
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.WineRegion)
|
|
||||||
class WineRegionAdmin(admin.ModelAdmin):
|
|
||||||
"""WineRegion admin."""
|
|
||||||
inlines = [WineAppellationInline, ]
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.WineAppellation)
|
|
||||||
class WineAppellationAdmin(admin.ModelAdmin):
|
|
||||||
"""WineAppellation admin."""
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.Address)
|
@admin.register(models.Address)
|
||||||
class AddressAdmin(admin.OSMGeoAdmin):
|
class AddressAdmin(admin.OSMGeoAdmin):
|
||||||
"""Address admin."""
|
"""Address admin."""
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,5 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(load_data_from_sql, revert_data),
|
# migrations.RunPython(load_data_from_sql, revert_data),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
69
apps/location/migrations/0021_auto_20191111_0731.py
Normal file
69
apps/location/migrations/0021_auto_20191111_0731.py
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-11-11 07:31
|
||||||
|
|
||||||
|
import django.contrib.gis.db.models.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('location', '0020_merge_20191030_1714'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WineSubRegion',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('old_id', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'wine region',
|
||||||
|
'verbose_name_plural': 'wine regions',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WineVillage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('old_id', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'wine village',
|
||||||
|
'verbose_name_plural': 'wine villages',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='wineappellation',
|
||||||
|
name='wine_region',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wineregion',
|
||||||
|
name='coordinates',
|
||||||
|
field=django.contrib.gis.db.models.fields.PointField(blank=True, default=None, null=True, srid=4326, verbose_name='Coordinates'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wineregion',
|
||||||
|
name='description',
|
||||||
|
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='description'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wineregion',
|
||||||
|
name='old_id',
|
||||||
|
field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='wineregion',
|
||||||
|
name='country',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='location.Country', verbose_name='country'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='wineregion',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='name'),
|
||||||
|
),
|
||||||
|
]
|
||||||
28
apps/location/migrations/0022_auto_20191111_0731.py
Normal file
28
apps/location/migrations/0022_auto_20191111_0731.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-11-11 07:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('location', '0021_auto_20191111_0731'),
|
||||||
|
('product', '0009_auto_20191111_0731'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='WineAppellation',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='winevillage',
|
||||||
|
name='wine_region',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='location.WineRegion', verbose_name='wine region'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='winesubregion',
|
||||||
|
name='wine_region',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='location.WineRegion', verbose_name='wine sub region'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -160,14 +160,19 @@ class WineRegionQuerySet(models.QuerySet):
|
||||||
"""Wine region queryset."""
|
"""Wine region queryset."""
|
||||||
|
|
||||||
|
|
||||||
class WineRegion(TranslatedFieldsMixin, models.Model):
|
class WineRegion(models.Model):
|
||||||
"""Wine region model."""
|
"""Wine region model."""
|
||||||
STR_FIELD_NAME = 'name'
|
name = models.CharField(_('name'), max_length=255)
|
||||||
|
|
||||||
name = TJSONField(verbose_name=_('Name'),
|
|
||||||
help_text='{"en-GB":"some text"}')
|
|
||||||
country = models.ForeignKey(Country, on_delete=models.PROTECT,
|
country = models.ForeignKey(Country, on_delete=models.PROTECT,
|
||||||
|
blank=True, null=True, default=None,
|
||||||
verbose_name=_('country'))
|
verbose_name=_('country'))
|
||||||
|
coordinates = models.PointField(
|
||||||
|
_('Coordinates'), blank=True, null=True, default=None)
|
||||||
|
old_id = models.PositiveIntegerField(_('old id'), default=None,
|
||||||
|
blank=True, null=True)
|
||||||
|
description = TJSONField(blank=True, null=True, default=None,
|
||||||
|
verbose_name=_('description'),
|
||||||
|
help_text='{"en-GB":"some text"}')
|
||||||
|
|
||||||
objects = WineRegionQuerySet.as_manager()
|
objects = WineRegionQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
@ -177,26 +182,47 @@ class WineRegion(TranslatedFieldsMixin, models.Model):
|
||||||
verbose_name = _('wine region')
|
verbose_name = _('wine region')
|
||||||
|
|
||||||
|
|
||||||
class WineAppellationQuerySet(models.QuerySet):
|
class WineSubRegionQuerySet(models.QuerySet):
|
||||||
"""Wine appellation queryset."""
|
"""Wine sub region QuerySet."""
|
||||||
|
|
||||||
|
|
||||||
class WineAppellation(TranslatedFieldsMixin, models.Model):
|
class WineSubRegion(models.Model):
|
||||||
"""Wine appellation model."""
|
"""Wine sub region model."""
|
||||||
STR_FIELD_NAME = 'name'
|
name = models.CharField(_('name'), max_length=255)
|
||||||
|
|
||||||
name = TJSONField(verbose_name=_('Name'),
|
|
||||||
help_text='{"en-GB":"some text"}')
|
|
||||||
wine_region = models.ForeignKey(WineRegion, on_delete=models.PROTECT,
|
wine_region = models.ForeignKey(WineRegion, on_delete=models.PROTECT,
|
||||||
related_name='appellations',
|
verbose_name=_('wine sub region'))
|
||||||
verbose_name=_('wine region'))
|
old_id = models.PositiveIntegerField(_('old id'), default=None,
|
||||||
|
blank=True, null=True)
|
||||||
|
|
||||||
objects = WineAppellationQuerySet.as_manager()
|
objects = WineSubRegionQuerySet.as_manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class."""
|
"""Meta class."""
|
||||||
verbose_name_plural = _('wine appellations')
|
verbose_name_plural = _('wine regions')
|
||||||
verbose_name = _('wine appellation')
|
verbose_name = _('wine region')
|
||||||
|
|
||||||
|
|
||||||
|
class WineVillageQuerySet(models.QuerySet):
|
||||||
|
"""Wine village QuerySet."""
|
||||||
|
|
||||||
|
|
||||||
|
class WineVillage(models.Model):
|
||||||
|
"""
|
||||||
|
Wine village.
|
||||||
|
Description: Imported from legacy DB.
|
||||||
|
"""
|
||||||
|
name = models.CharField(_('name'), max_length=255)
|
||||||
|
wine_region = models.ForeignKey(WineRegion, on_delete=models.PROTECT,
|
||||||
|
verbose_name=_('wine region'))
|
||||||
|
old_id = models.PositiveIntegerField(_('old id'), default=None,
|
||||||
|
blank=True, null=True)
|
||||||
|
|
||||||
|
objects = WineVillageQuerySet.as_manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
verbose_name = _('wine village')
|
||||||
|
verbose_name_plural = _('wine villages')
|
||||||
|
|
||||||
|
|
||||||
# todo: Make recalculate price levels
|
# todo: Make recalculate price levels
|
||||||
|
|
|
||||||
|
|
@ -152,19 +152,6 @@ class AddressDetailSerializer(AddressBaseSerializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WineAppellationBaseSerializer(serializers.ModelSerializer):
|
|
||||||
"""Wine appellations."""
|
|
||||||
name_translated = TranslatedField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta class."""
|
|
||||||
model = models.WineAppellation
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'name_translated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WineRegionBaseSerializer(serializers.ModelSerializer):
|
class WineRegionBaseSerializer(serializers.ModelSerializer):
|
||||||
"""Wine region serializer."""
|
"""Wine region serializer."""
|
||||||
name_translated = TranslatedField()
|
name_translated = TranslatedField()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
from transfer.serializers.location import CountrySerializer, RegionSerializer, \
|
from transfer.serializers import location as location_serializers
|
||||||
CitySerializer, AddressSerializer, \
|
from transfer import models as transfer_models
|
||||||
Country
|
from location.models import Country
|
||||||
from transfer.models import Cities, Locations
|
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
from requests import get
|
from requests import get
|
||||||
|
|
||||||
|
|
||||||
def transfer_countries():
|
def transfer_countries():
|
||||||
queryset = Cities.objects.raw("""
|
queryset = transfer_models.Cities.objects.raw("""
|
||||||
SELECT cities.id, cities.country_code_2
|
SELECT cities.id, cities.country_code_2
|
||||||
FROM cities
|
FROM cities
|
||||||
WHERE country_code_2 IS NOT NULL AND
|
WHERE country_code_2 IS NOT NULL AND
|
||||||
|
|
@ -18,7 +17,7 @@ def transfer_countries():
|
||||||
|
|
||||||
queryset = [vars(query) for query in queryset]
|
queryset = [vars(query) for query in queryset]
|
||||||
|
|
||||||
serialized_data = CountrySerializer(data=queryset, many=True)
|
serialized_data = location_serializers.CountrySerializer(data=queryset, many=True)
|
||||||
if serialized_data.is_valid():
|
if serialized_data.is_valid():
|
||||||
serialized_data.save()
|
serialized_data.save()
|
||||||
else:
|
else:
|
||||||
|
|
@ -26,7 +25,7 @@ def transfer_countries():
|
||||||
|
|
||||||
|
|
||||||
def transfer_regions():
|
def transfer_regions():
|
||||||
regions_without_subregion_queryset = Cities.objects.raw("""
|
regions_without_subregion_queryset = transfer_models.Cities.objects.raw("""
|
||||||
SELECT cities.id,
|
SELECT cities.id,
|
||||||
cities.region_code,
|
cities.region_code,
|
||||||
cities.country_code_2,
|
cities.country_code_2,
|
||||||
|
|
@ -47,13 +46,14 @@ def transfer_regions():
|
||||||
|
|
||||||
regions_without_subregion_queryset = [vars(query) for query in regions_without_subregion_queryset]
|
regions_without_subregion_queryset = [vars(query) for query in regions_without_subregion_queryset]
|
||||||
|
|
||||||
serialized_without_subregion = RegionSerializer(data=regions_without_subregion_queryset, many=True)
|
serialized_without_subregion = location_serializers.RegionSerializer(data=regions_without_subregion_queryset,
|
||||||
|
many=True)
|
||||||
if serialized_without_subregion.is_valid():
|
if serialized_without_subregion.is_valid():
|
||||||
serialized_without_subregion.save()
|
serialized_without_subregion.save()
|
||||||
else:
|
else:
|
||||||
pprint(f"Parent regions serializer errors: {serialized_without_subregion.errors}")
|
pprint(f"Parent regions serializer errors: {serialized_without_subregion.errors}")
|
||||||
|
|
||||||
regions_with_subregion_queryset = Cities.objects.raw("""
|
regions_with_subregion_queryset = transfer_models.Cities.objects.raw("""
|
||||||
SELECT
|
SELECT
|
||||||
cities.id,
|
cities.id,
|
||||||
cities.region_code,
|
cities.region_code,
|
||||||
|
|
@ -84,7 +84,8 @@ def transfer_regions():
|
||||||
|
|
||||||
regions_with_subregion_queryset = [vars(query) for query in regions_with_subregion_queryset]
|
regions_with_subregion_queryset = [vars(query) for query in regions_with_subregion_queryset]
|
||||||
|
|
||||||
serialized_with_subregion = RegionSerializer(data=regions_with_subregion_queryset, many=True)
|
serialized_with_subregion = location_serializers.RegionSerializer(data=regions_with_subregion_queryset,
|
||||||
|
many=True)
|
||||||
if serialized_with_subregion.is_valid():
|
if serialized_with_subregion.is_valid():
|
||||||
serialized_with_subregion.save()
|
serialized_with_subregion.save()
|
||||||
else:
|
else:
|
||||||
|
|
@ -92,7 +93,7 @@ def transfer_regions():
|
||||||
|
|
||||||
|
|
||||||
def transfer_cities():
|
def transfer_cities():
|
||||||
queryset = Cities.objects.raw("""SELECT cities.id, cities.name, cities.country_code_2, cities.zip_code,
|
queryset = transfer_models.Cities.objects.raw("""SELECT cities.id, cities.name, cities.country_code_2, cities.zip_code,
|
||||||
cities.is_island, cities.region_code, cities.subregion_code
|
cities.is_island, cities.region_code, cities.subregion_code
|
||||||
FROM cities WHERE
|
FROM cities WHERE
|
||||||
region_code IS NOT NULL AND
|
region_code IS NOT NULL AND
|
||||||
|
|
@ -103,7 +104,7 @@ def transfer_cities():
|
||||||
|
|
||||||
queryset = [vars(query) for query in queryset]
|
queryset = [vars(query) for query in queryset]
|
||||||
|
|
||||||
serialized_data = CitySerializer(data=queryset, many=True)
|
serialized_data = location_serializers.CitySerializer(data=queryset, many=True)
|
||||||
if serialized_data.is_valid():
|
if serialized_data.is_valid():
|
||||||
serialized_data.save()
|
serialized_data.save()
|
||||||
else:
|
else:
|
||||||
|
|
@ -111,7 +112,7 @@ def transfer_cities():
|
||||||
|
|
||||||
|
|
||||||
def transfer_addresses():
|
def transfer_addresses():
|
||||||
queryset = Locations.objects.raw("""SELECT locations.id, locations.zip_code, locations.longitude,
|
queryset = transfer_models.Locations.objects.raw("""SELECT locations.id, locations.zip_code, locations.longitude,
|
||||||
locations.latitude, locations.address, locations.city_id
|
locations.latitude, locations.address, locations.city_id
|
||||||
FROM locations WHERE
|
FROM locations WHERE
|
||||||
locations.address != "" AND
|
locations.address != "" AND
|
||||||
|
|
@ -126,13 +127,46 @@ def transfer_addresses():
|
||||||
|
|
||||||
queryset = [vars(query) for query in queryset]
|
queryset = [vars(query) for query in queryset]
|
||||||
|
|
||||||
serialized_data = AddressSerializer(data=queryset, many=True)
|
serialized_data = location_serializers.AddressSerializer(data=queryset, many=True)
|
||||||
if serialized_data.is_valid():
|
if serialized_data.is_valid():
|
||||||
serialized_data.save()
|
serialized_data.save()
|
||||||
else:
|
else:
|
||||||
pprint(f"Address serializer errors: {serialized_data.errors}")
|
pprint(f"Address serializer errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_region():
|
||||||
|
queryset = transfer_models.WineLocations.objects.filter(type='WineRegion')
|
||||||
|
serialized_data = location_serializers.WineRegion(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"WineStandardClassificationSerializer errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_sub_region():
|
||||||
|
queryset = transfer_models.WineLocations.objects.filter(type='WineSubRegion')
|
||||||
|
serialized_data = location_serializers.WineSubRegion(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"WineStandardClassificationSerializer errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_village():
|
||||||
|
queryset = transfer_models.WineLocations.objects.filter(type='Village')
|
||||||
|
serialized_data = location_serializers.WineVillage(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"WineStandardClassificationSerializer errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
def update_flags():
|
def update_flags():
|
||||||
queryset = Country.objects.only("id", "code", "svg_image").filter(old_id__isnull=False)
|
queryset = Country.objects.only("id", "code", "svg_image").filter(old_id__isnull=False)
|
||||||
link_to_request = "https://s3.eu-central-1.amazonaws.com/gm-test.com/media"
|
link_to_request = "https://s3.eu-central-1.amazonaws.com/gm-test.com/media"
|
||||||
|
|
@ -150,9 +184,12 @@ data_types = {
|
||||||
transfer_countries,
|
transfer_countries,
|
||||||
transfer_regions,
|
transfer_regions,
|
||||||
transfer_cities,
|
transfer_cities,
|
||||||
transfer_addresses
|
transfer_addresses,
|
||||||
|
transfer_wine_region,
|
||||||
|
transfer_wine_sub_region,
|
||||||
|
transfer_wine_village,
|
||||||
],
|
],
|
||||||
"update_country_flag": [
|
"update_country_flag": [
|
||||||
update_flags
|
update_flags
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
"""Product admin conf."""
|
"""Product admin conf."""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from utils.admin import BaseModelAdminMixin
|
from utils.admin import BaseModelAdminMixin
|
||||||
from .models import Product, ProductType, ProductSubType
|
from .models import Product, ProductType, ProductSubType, Unit
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Product)
|
@admin.register(Product)
|
||||||
class ProductAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
class ProductAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
||||||
"""Admin page for model Product."""
|
"""Admin page for model Product."""
|
||||||
|
search_fields = ('name', )
|
||||||
|
list_filter = ('available', 'product_type')
|
||||||
|
list_display = ('id', '__str__', 'get_category_display', 'product_type')
|
||||||
|
raw_id_fields = ('subtypes', 'classifications', 'standards',
|
||||||
|
'tags', 'gallery')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ProductType)
|
@admin.register(ProductType)
|
||||||
|
|
@ -17,3 +22,8 @@ class ProductTypeAdmin(admin.ModelAdmin):
|
||||||
@admin.register(ProductSubType)
|
@admin.register(ProductSubType)
|
||||||
class ProductSubTypeAdmin(admin.ModelAdmin):
|
class ProductSubTypeAdmin(admin.ModelAdmin):
|
||||||
"""Admin page for model ProductSubType."""
|
"""Admin page for model ProductSubType."""
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Unit)
|
||||||
|
class UnitAdmin(admin.ModelAdmin):
|
||||||
|
"""Admin page for model Unit."""
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from product.models import ProductType, ProductSubType
|
|
||||||
|
|
||||||
|
|
||||||
def add_type():
|
|
||||||
product_type = ProductType.objects.create(
|
|
||||||
name={'"en-GB"': "Wine"},
|
|
||||||
index_name=ProductType.WINE
|
|
||||||
)
|
|
||||||
return product_type.save()
|
|
||||||
|
|
||||||
|
|
||||||
def add_subtype(id_type):
|
|
||||||
subtypes = ProductSubType.objects.bulk_create([
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.EXTRA_BRUT},
|
|
||||||
index_name=ProductSubType.EXTRA_BRUT),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.BRUT},
|
|
||||||
index_name=ProductSubType.BRUT),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.BRUT_NATURE},
|
|
||||||
index_name=ProductSubType.BRUT_NATURE),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.DEMI_SEC},
|
|
||||||
index_name=ProductSubType.DEMI_SEC),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.EXTRA_DRY},
|
|
||||||
index_name=ProductSubType.EXTRA_DRY),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.DOSAGE_ZERO},
|
|
||||||
index_name=ProductSubType.DOSAGE_ZERO),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.SEC},
|
|
||||||
index_name=ProductSubType.SEC),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.SEC},
|
|
||||||
index_name=ProductSubType.SEC),
|
|
||||||
ProductSubType(product_type=id_type,
|
|
||||||
name={"en-GB", ProductSubType.MOELLEUX},
|
|
||||||
index_name=ProductSubType.MOELLEUX),
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Add product data'
|
|
||||||
|
|
||||||
def handle(self, *args, **kwarg):
|
|
||||||
product_type = add_type()
|
|
||||||
add_subtype(product_type.id)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
34
apps/product/management/commands/add_product_sub_type.py
Normal file
34
apps/product/management/commands/add_product_sub_type.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from product.models import ProductType, ProductSubType
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Add product sub type data'
|
||||||
|
|
||||||
|
def handle(self, *args, **kwarg):
|
||||||
|
error_counter = 0
|
||||||
|
create_counter = 0
|
||||||
|
|
||||||
|
product_subtypes = {
|
||||||
|
'plate': {
|
||||||
|
'product_type_index_name': 'souvenir',
|
||||||
|
'name': 'plate',
|
||||||
|
'index_name': 'plate',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for product_subtype in product_subtypes.values():
|
||||||
|
product_type_qs = ProductType.objects.filter(
|
||||||
|
index_name=product_subtype.get('product_type_index_name'))
|
||||||
|
if product_type_qs:
|
||||||
|
product_type = product_type_qs.first()
|
||||||
|
subtype, status = ProductSubType.objects.get_or_create(**{
|
||||||
|
'name': {settings.FALLBACK_LOCALE: product_subtype.get('name')},
|
||||||
|
'index_name': product_subtype.get('index_name'),
|
||||||
|
'product_type': product_type
|
||||||
|
})
|
||||||
|
create_counter += 1 if status else 0
|
||||||
|
else:
|
||||||
|
error_counter += 1
|
||||||
|
self.stdout.write(self.style.WARNING(f'Errors occurred: {error_counter}\nCreated: {create_counter}'))
|
||||||
20
apps/product/management/commands/add_product_type.py
Normal file
20
apps/product/management/commands/add_product_type.py
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from product.models import ProductType
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Add product type data'
|
||||||
|
|
||||||
|
def handle(self, *args, **kwarg):
|
||||||
|
product_types = ['wine', 'souvenir']
|
||||||
|
create_counter = 0
|
||||||
|
for product_type in product_types:
|
||||||
|
subtype, created = ProductType.objects.get_or_create(**{
|
||||||
|
'name': {settings.FALLBACK_LOCALE: product_type},
|
||||||
|
'index_name': product_type
|
||||||
|
})
|
||||||
|
if created:
|
||||||
|
created += 1
|
||||||
|
self.stdout.write(self.style.WARNING(f'Created: {create_counter}'))
|
||||||
179
apps/product/migrations/0009_auto_20191111_0731.py
Normal file
179
apps/product/migrations/0009_auto_20191111_0731.py
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-11-11 07:31
|
||||||
|
|
||||||
|
import django.contrib.gis.db.models.fields
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gallery', '0006_merge_20191027_1758'),
|
||||||
|
('location', '0021_auto_20191111_0731'),
|
||||||
|
('tag', '0009_auto_20191111_0731'),
|
||||||
|
('product', '0008_auto_20191031_1410'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProductStandard',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('standard_type', models.PositiveSmallIntegerField(choices=[(0, 'Appellation'), (1, 'Wine quality'), (2, 'Yard classification')], verbose_name='standard type')),
|
||||||
|
('coordinates', django.contrib.gis.db.models.fields.PointField(blank=True, default=None, null=True, srid=4326, verbose_name='Coordinates')),
|
||||||
|
('old_id', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'wine standard',
|
||||||
|
'verbose_name_plural': 'wine standards',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Unit',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('value', models.CharField(max_length=255, verbose_name='value')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'unit',
|
||||||
|
'verbose_name_plural': 'units',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='product',
|
||||||
|
name='characteristics',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='product',
|
||||||
|
name='wine_appellation',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='old_id',
|
||||||
|
field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='old_unique_key',
|
||||||
|
field=models.CharField(blank=True, default=None, help_text='attribute from legacy db', max_length=255, null=True, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='state',
|
||||||
|
field=models.PositiveIntegerField(choices=[(0, 'Published'), (1, 'Out_of_production'), (2, 'Waiting')], default=2, help_text='attribute from legacy db', verbose_name='state'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='tags',
|
||||||
|
field=models.ManyToManyField(related_name='products', to='tag.Tag', verbose_name='Tag'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='vintage',
|
||||||
|
field=models.IntegerField(blank=True, default=None, null=True, validators=[django.core.validators.MinValueValidator(1700), django.core.validators.MaxValueValidator(2100)], verbose_name='vintage year'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='wine_sub_region',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='wines', to='location.WineSubRegion', verbose_name='wine sub region'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='wine_village',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='location.WineVillage', verbose_name='wine appellation'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='producttype',
|
||||||
|
name='tag_categories',
|
||||||
|
field=models.ManyToManyField(related_name='product_types', to='tag.TagCategory', verbose_name='Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='establishment',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='products', to='establishment.Establishment', verbose_name='establishment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(default=None, max_length=255, null=True, verbose_name='name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='product_type',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='products', to='product.ProductType', verbose_name='Type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='wine_region',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='wines', to='location.WineRegion', verbose_name='wine region'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='productsubtype',
|
||||||
|
name='index_name',
|
||||||
|
field=models.CharField(choices=[('rum', 'Rum'), ('plate', 'Plate'), ('other', 'Other')], db_index=True, max_length=50, unique=True, verbose_name='Index name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='producttype',
|
||||||
|
name='index_name',
|
||||||
|
field=models.CharField(choices=[('food', 'Food'), ('wine', 'Wine'), ('liquor', 'Liquor'), ('souvenir', 'Souvenir'), ('book', 'Book')], db_index=True, max_length=50, unique=True, verbose_name='Index name'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProductGallery',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('is_main', models.BooleanField(default=False, verbose_name='Is the main image')),
|
||||||
|
('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_gallery', to='gallery.Image', verbose_name='gallery')),
|
||||||
|
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_gallery', to='product.Product', verbose_name='product')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'product gallery',
|
||||||
|
'verbose_name_plural': 'product galleries',
|
||||||
|
'unique_together': {('product', 'image'), ('product', 'is_main')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProductClassificationType',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, unique=True, verbose_name='classification type')),
|
||||||
|
('product_sub_type', models.ForeignKey(blank=True, default=None, help_text='Legacy attribute - possible_type (product type).Product type in our case is product subtype.', null=True, on_delete=django.db.models.deletion.PROTECT, to='product.ProductSubType', verbose_name='product subtype')),
|
||||||
|
('product_type', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='product.ProductType', verbose_name='product type')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'wine classification type',
|
||||||
|
'verbose_name_plural': 'wine classification types',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProductClassification',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('old_id', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id')),
|
||||||
|
('classification_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.ProductClassificationType', verbose_name='classification type')),
|
||||||
|
('standard', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='product.ProductStandard', verbose_name='standard')),
|
||||||
|
('tags', models.ManyToManyField(related_name='product_classifications', to='tag.Tag', verbose_name='Tag')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'product classification',
|
||||||
|
'verbose_name_plural': 'product classifications',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='classifications',
|
||||||
|
field=models.ManyToManyField(blank=True, to='product.ProductClassification', verbose_name='classifications'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='gallery',
|
||||||
|
field=models.ManyToManyField(through='product.ProductGallery', to='gallery.Image'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='standards',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='attribute from legacy db', to='product.ProductStandard', verbose_name='standards'),
|
||||||
|
),
|
||||||
|
]
|
||||||
33
apps/product/migrations/0010_auto_20191111_1227.py
Normal file
33
apps/product/migrations/0010_auto_20191111_1227.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-11-11 12:27
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('product', '0009_auto_20191111_0731'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='product',
|
||||||
|
name='country',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='available',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='available'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='description',
|
||||||
|
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(max_length=255, null=True, unique=True, verbose_name='Slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
"""Product app models."""
|
"""Product app models."""
|
||||||
from django.db import models
|
|
||||||
from django.contrib.contenttypes import fields as generic
|
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.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 django.utils.translation import gettext_lazy as _
|
||||||
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
|
|
||||||
from utils.models import (BaseAttributes, ProjectBaseMixin,
|
from utils.models import (BaseAttributes, ProjectBaseMixin,
|
||||||
TranslatedFieldsMixin, TJSONField)
|
TranslatedFieldsMixin, TJSONField)
|
||||||
|
|
||||||
|
|
@ -17,11 +19,15 @@ class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
|
||||||
FOOD = 'food'
|
FOOD = 'food'
|
||||||
WINE = 'wine'
|
WINE = 'wine'
|
||||||
LIQUOR = 'liquor'
|
LIQUOR = 'liquor'
|
||||||
|
SOUVENIR = 'souvenir'
|
||||||
|
BOOK = 'book'
|
||||||
|
|
||||||
INDEX_NAME_TYPES = (
|
INDEX_NAME_TYPES = (
|
||||||
(FOOD, _('Food')),
|
(FOOD, _('Food')),
|
||||||
(WINE, _('Wine')),
|
(WINE, _('Wine')),
|
||||||
(LIQUOR, _('Liquor')),
|
(LIQUOR, _('Liquor')),
|
||||||
|
(SOUVENIR, _('Souvenir')),
|
||||||
|
(BOOK, _('Book')),
|
||||||
)
|
)
|
||||||
|
|
||||||
name = TJSONField(blank=True, null=True, default=None,
|
name = TJSONField(blank=True, null=True, default=None,
|
||||||
|
|
@ -30,6 +36,9 @@ class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
|
||||||
unique=True, db_index=True,
|
unique=True, db_index=True,
|
||||||
verbose_name=_('Index name'))
|
verbose_name=_('Index name'))
|
||||||
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
|
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
|
||||||
|
tag_categories = models.ManyToManyField('tag.TagCategory',
|
||||||
|
related_name='product_types',
|
||||||
|
verbose_name=_('Tag'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class."""
|
"""Meta class."""
|
||||||
|
|
@ -45,29 +54,13 @@ class ProductSubType(TranslatedFieldsMixin, ProjectBaseMixin):
|
||||||
|
|
||||||
# INDEX NAME CHOICES
|
# INDEX NAME CHOICES
|
||||||
RUM = 'rum'
|
RUM = 'rum'
|
||||||
|
PLATE = 'plate'
|
||||||
OTHER = 'other'
|
OTHER = 'other'
|
||||||
EXTRA_BRUT = 'extra brut'
|
|
||||||
BRUT = 'brut'
|
|
||||||
BRUT_NATURE = 'brut nature'
|
|
||||||
DEMI_SEC = 'demi-sec'
|
|
||||||
EXTRA_DRY = 'Extra Dry'
|
|
||||||
DOSAGE_ZERO = 'dosage zero'
|
|
||||||
SEC = 'sec'
|
|
||||||
DOUX = 'doux'
|
|
||||||
MOELLEUX= 'moelleux'
|
|
||||||
|
|
||||||
INDEX_NAME_TYPES = (
|
INDEX_NAME_TYPES = (
|
||||||
(RUM, _('Rum')),
|
(RUM, _('Rum')),
|
||||||
|
(PLATE, _('Plate')),
|
||||||
(OTHER, _('Other')),
|
(OTHER, _('Other')),
|
||||||
(EXTRA_BRUT, _('extra brut')),
|
|
||||||
(BRUT, _('brut')),
|
|
||||||
(BRUT_NATURE, _('brut nature')),
|
|
||||||
(DEMI_SEC, _('demi-sec')),
|
|
||||||
(EXTRA_DRY, _('Extra Dry')),
|
|
||||||
(DOSAGE_ZERO, _('dosage zero')),
|
|
||||||
(SEC, _('sec')),
|
|
||||||
(DOUX, _('doux')),
|
|
||||||
(MOELLEUX, _('moelleux'))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE,
|
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE,
|
||||||
|
|
@ -99,7 +92,7 @@ class ProductQuerySet(models.QuerySet):
|
||||||
|
|
||||||
def with_base_related(self):
|
def with_base_related(self):
|
||||||
return self.select_related('product_type', 'establishment') \
|
return self.select_related('product_type', 'establishment') \
|
||||||
.prefetch_related('product_type__subtypes', 'country')
|
.prefetch_related('product_type__subtypes')
|
||||||
|
|
||||||
def common(self):
|
def common(self):
|
||||||
return self.filter(category=self.model.COMMON)
|
return self.filter(category=self.model.COMMON)
|
||||||
|
|
@ -122,7 +115,8 @@ class ProductQuerySet(models.QuerySet):
|
||||||
class Product(TranslatedFieldsMixin, BaseAttributes):
|
class Product(TranslatedFieldsMixin, BaseAttributes):
|
||||||
"""Product models."""
|
"""Product models."""
|
||||||
|
|
||||||
STR_FIELD_NAME = 'name'
|
EARLIEST_VINTAGE_YEAR = 1700
|
||||||
|
LATEST_VINTAGE_YEAR = 2100
|
||||||
|
|
||||||
COMMON = 0
|
COMMON = 0
|
||||||
ONLINE = 1
|
ONLINE = 1
|
||||||
|
|
@ -132,38 +126,74 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
|
||||||
(ONLINE, _('Online')),
|
(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,
|
category = models.PositiveIntegerField(choices=CATEGORY_CHOICES,
|
||||||
default=COMMON)
|
default=COMMON)
|
||||||
name = TJSONField(_('Name'), null=True, blank=True, default=None,
|
name = models.CharField(max_length=255,
|
||||||
help_text='{"en-GB":"some text"}')
|
default=None, null=True,
|
||||||
description = TJSONField(_('Description'), null=True, blank=True,
|
verbose_name=_('name'))
|
||||||
|
description = TJSONField(_('description'), null=True, blank=True,
|
||||||
default=None, help_text='{"en-GB":"some text"}')
|
default=None, help_text='{"en-GB":"some text"}')
|
||||||
#TODO set null=False
|
available = models.BooleanField(_('available'), default=True)
|
||||||
characteristics = JSONField(_('Characteristics'), null=True)
|
|
||||||
country = models.ManyToManyField('location.Country',
|
|
||||||
verbose_name=_('Country'))
|
|
||||||
available = models.BooleanField(_('Available'), default=True)
|
|
||||||
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT,
|
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT,
|
||||||
|
null=True,
|
||||||
related_name='products', verbose_name=_('Type'))
|
related_name='products', verbose_name=_('Type'))
|
||||||
subtypes = models.ManyToManyField(ProductSubType, blank=True,
|
subtypes = models.ManyToManyField(ProductSubType, blank=True,
|
||||||
related_name='products',
|
related_name='products',
|
||||||
verbose_name=_('Subtypes'))
|
verbose_name=_('Subtypes'))
|
||||||
establishment = models.ForeignKey('establishment.Establishment',
|
establishment = models.ForeignKey('establishment.Establishment', on_delete=models.PROTECT,
|
||||||
on_delete=models.PROTECT,
|
blank=True, null=True,
|
||||||
related_name='products',
|
related_name='products',
|
||||||
verbose_name=_('establishment'))
|
verbose_name=_('establishment'))
|
||||||
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
|
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
|
||||||
verbose_name=_('public mark'),)
|
verbose_name=_('public mark'),)
|
||||||
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.PROTECT,
|
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.PROTECT,
|
||||||
related_name='wines',
|
related_name='wines',
|
||||||
blank=True, null=True,
|
blank=True, null=True, default=None,
|
||||||
verbose_name=_('wine region'))
|
verbose_name=_('wine region'))
|
||||||
wine_appellation = models.ForeignKey('location.WineAppellation', on_delete=models.PROTECT,
|
wine_sub_region = models.ForeignKey('location.WineSubRegion', on_delete=models.PROTECT,
|
||||||
blank=True, null=True,
|
related_name='wines',
|
||||||
verbose_name=_('wine appellation'))
|
blank=True, null=True, default=None,
|
||||||
|
verbose_name=_('wine sub region'))
|
||||||
|
classifications = models.ManyToManyField('ProductClassification',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_('classifications'))
|
||||||
|
standards = models.ManyToManyField('ProductStandard',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_('standards'),
|
||||||
|
help_text=_('attribute from legacy db'))
|
||||||
|
wine_village = models.ForeignKey('location.WineVillage', on_delete=models.PROTECT,
|
||||||
|
blank=True, null=True,
|
||||||
|
verbose_name=_('wine appellation'))
|
||||||
slug = models.SlugField(unique=True, max_length=255, null=True,
|
slug = models.SlugField(unique=True, max_length=255, null=True,
|
||||||
verbose_name=_('Establishment slug'))
|
verbose_name=_('Slug'))
|
||||||
favorites = generic.GenericRelation(to='favorites.Favorites')
|
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'))
|
||||||
|
tags = models.ManyToManyField('tag.Tag', related_name='products',
|
||||||
|
verbose_name=_('Tag'))
|
||||||
|
old_unique_key = models.CharField(max_length=255, unique=True,
|
||||||
|
blank=True, null=True, default=None,
|
||||||
|
help_text=_('attribute from legacy db'))
|
||||||
|
vintage = models.IntegerField(verbose_name=_('vintage year'),
|
||||||
|
null=True, blank=True, default=None,
|
||||||
|
validators=[MinValueValidator(EARLIEST_VINTAGE_YEAR),
|
||||||
|
MaxValueValidator(LATEST_VINTAGE_YEAR)])
|
||||||
|
gallery = models.ManyToManyField('gallery.Image', through='ProductGallery')
|
||||||
|
reviews = generic.GenericRelation(to='review.Review')
|
||||||
|
|
||||||
objects = ProductManager.from_queryset(ProductQuerySet)()
|
objects = ProductManager.from_queryset(ProductQuerySet)()
|
||||||
|
|
||||||
|
|
@ -173,15 +203,19 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
|
||||||
verbose_name = _('Product')
|
verbose_name = _('Product')
|
||||||
verbose_name_plural = _('Products')
|
verbose_name_plural = _('Products')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Override str dunder method."""
|
||||||
|
return f'{self.name}'
|
||||||
|
|
||||||
def clean_fields(self, exclude=None):
|
def clean_fields(self, exclude=None):
|
||||||
super().clean_fields(exclude=exclude)
|
super().clean_fields(exclude=exclude)
|
||||||
if self.product_type.index_name == ProductType.WINE and not self.wine_region:
|
if self.product_type.index_name == ProductType.WINE and not self.wine_region:
|
||||||
raise ValidationError(_('wine_region field must be specified.'))
|
raise ValidationError(_('wine_region field must be specified.'))
|
||||||
if not self.product_type.index_name == ProductType.WINE and self.wine_region:
|
if not self.product_type.index_name == ProductType.WINE and self.wine_region:
|
||||||
raise ValidationError(_('wine_region field must not be specified.'))
|
raise ValidationError(_('wine_region field must not be specified.'))
|
||||||
if (self.wine_region and self.wine_appellation) and \
|
# if (self.wine_region and self.wine_appellation) and \
|
||||||
self.wine_appellation not in self.wine_region.appellations.all():
|
# self.wine_appellation not in self.wine_region.appellations.all():
|
||||||
raise ValidationError(_('Wine appellation not exists in wine region.'))
|
# raise ValidationError(_('Wine appellation not exists in wine region.'))
|
||||||
|
|
||||||
|
|
||||||
class OnlineProductManager(ProductManager):
|
class OnlineProductManager(ProductManager):
|
||||||
|
|
@ -203,3 +237,129 @@ class OnlineProduct(Product):
|
||||||
proxy = True
|
proxy = True
|
||||||
verbose_name = _('Online product')
|
verbose_name = _('Online product')
|
||||||
verbose_name_plural = _('Online products')
|
verbose_name_plural = _('Online products')
|
||||||
|
|
||||||
|
|
||||||
|
class Unit(models.Model):
|
||||||
|
"""Product unit model."""
|
||||||
|
name = models.CharField(max_length=255,
|
||||||
|
verbose_name=_('name'))
|
||||||
|
value = models.CharField(max_length=255,
|
||||||
|
verbose_name=_('value'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
verbose_name = _('unit')
|
||||||
|
verbose_name_plural = _('units')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Overridden dunder method."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ProductStandardQuerySet(models.QuerySet):
|
||||||
|
"""Product standard queryset."""
|
||||||
|
|
||||||
|
|
||||||
|
class ProductStandard(models.Model):
|
||||||
|
"""Product standard model."""
|
||||||
|
|
||||||
|
APPELLATION = 0
|
||||||
|
WINEQUALITY = 1
|
||||||
|
YARDCLASSIFICATION = 2
|
||||||
|
|
||||||
|
STANDARDS = (
|
||||||
|
(APPELLATION, _('Appellation')),
|
||||||
|
(WINEQUALITY, _('Wine quality')),
|
||||||
|
(YARDCLASSIFICATION, _('Yard classification')),
|
||||||
|
)
|
||||||
|
|
||||||
|
name = models.CharField(_('name'), max_length=255)
|
||||||
|
standard_type = models.PositiveSmallIntegerField(choices=STANDARDS,
|
||||||
|
verbose_name=_('standard type'))
|
||||||
|
coordinates = gis_models.PointField(
|
||||||
|
_('Coordinates'), blank=True, null=True, default=None)
|
||||||
|
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||||
|
|
||||||
|
objects = ProductStandardQuerySet.as_manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
verbose_name_plural = _('wine standards')
|
||||||
|
verbose_name = _('wine standard')
|
||||||
|
|
||||||
|
|
||||||
|
class ProductGalleryQuerySet(models.QuerySet):
|
||||||
|
"""QuerySet for model Product"""
|
||||||
|
|
||||||
|
def main_image(self):
|
||||||
|
"""Return objects with flag is_main is True"""
|
||||||
|
return self.filter(is_main=True)
|
||||||
|
|
||||||
|
|
||||||
|
class ProductGallery(models.Model):
|
||||||
|
product = models.ForeignKey(Product, null=True,
|
||||||
|
related_name='product_gallery',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('product'))
|
||||||
|
image = models.ForeignKey('gallery.Image', null=True,
|
||||||
|
related_name='product_gallery',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('gallery'))
|
||||||
|
is_main = models.BooleanField(default=False,
|
||||||
|
verbose_name=_('Is the main image'))
|
||||||
|
|
||||||
|
objects = ProductGalleryQuerySet.as_manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""ProductGallery meta class."""
|
||||||
|
verbose_name = _('product gallery')
|
||||||
|
verbose_name_plural = _('product galleries')
|
||||||
|
unique_together = (('product', 'is_main'), ('product', 'image'))
|
||||||
|
|
||||||
|
|
||||||
|
class ProductClassificationType(models.Model):
|
||||||
|
"""Product classification type."""
|
||||||
|
|
||||||
|
name = models.CharField(max_length=255, unique=True,
|
||||||
|
verbose_name=_('classification type'))
|
||||||
|
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT,
|
||||||
|
null=True, default=None,
|
||||||
|
verbose_name=_('product type'))
|
||||||
|
product_sub_type = models.ForeignKey(ProductSubType, on_delete=models.PROTECT,
|
||||||
|
blank=True, null=True, default=None,
|
||||||
|
verbose_name=_('product subtype'),
|
||||||
|
help_text=_('Legacy attribute - possible_type (product type).'
|
||||||
|
'Product type in our case is product subtype.'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
verbose_name = _('wine classification type')
|
||||||
|
verbose_name_plural = _('wine classification types')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Override str dunder."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ProductClassificationQuerySet(models.QuerySet):
|
||||||
|
"""Product classification QuerySet."""
|
||||||
|
|
||||||
|
|
||||||
|
class ProductClassification(models.Model):
|
||||||
|
"""Product classification model."""
|
||||||
|
|
||||||
|
classification_type = models.ForeignKey(ProductClassificationType, on_delete=models.PROTECT,
|
||||||
|
verbose_name=_('classification type'))
|
||||||
|
standard = models.ForeignKey(ProductStandard, on_delete=models.PROTECT,
|
||||||
|
null=True, blank=True, default=None,
|
||||||
|
verbose_name=_('standard'))
|
||||||
|
tags = models.ManyToManyField('tag.Tag', related_name='product_classifications',
|
||||||
|
verbose_name=_('Tag'))
|
||||||
|
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||||
|
|
||||||
|
objects = ProductClassificationQuerySet.as_manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
verbose_name = _('product classification')
|
||||||
|
verbose_name_plural = _('product classifications')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
from .common import *
|
from .common import *
|
||||||
from .web import *
|
from .web import *
|
||||||
from .mobile import *
|
from .mobile import *
|
||||||
|
from .back import *
|
||||||
|
|
|
||||||
44
apps/product/serializers/back.py
Normal file
44
apps/product/serializers/back.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
"""Product app back-office serializers."""
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from product import models
|
||||||
|
from gallery.models import Image
|
||||||
|
|
||||||
|
|
||||||
|
class ProductBackOfficeGallerySerializer(serializers.ModelSerializer):
|
||||||
|
"""Serializer class for model ProductGallery."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class"""
|
||||||
|
|
||||||
|
model = models.ProductGallery
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'is_main',
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_request_kwargs(self):
|
||||||
|
"""Get url kwargs from request."""
|
||||||
|
return self.context.get('request').parser_context.get('kwargs')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""Override validate method."""
|
||||||
|
product_pk = self.get_request_kwargs().get('pk')
|
||||||
|
image_id = self.get_request_kwargs().get('image_id')
|
||||||
|
|
||||||
|
product_qs = models.Product.objects.filter(pk=product_pk)
|
||||||
|
image_qs = Image.objects.filter(id=image_id)
|
||||||
|
|
||||||
|
if not product_qs.exists():
|
||||||
|
raise serializers.ValidationError({'detail': _('Product not found')})
|
||||||
|
if not image_qs.exists():
|
||||||
|
raise serializers.ValidationError({'detail': _('Image not found')})
|
||||||
|
|
||||||
|
product = product_qs.first()
|
||||||
|
image = image_qs.first()
|
||||||
|
|
||||||
|
attrs['product'] = product
|
||||||
|
attrs['image'] = image
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
@ -4,8 +4,8 @@ from utils.serializers import TranslatedField, FavoritesCreateSerializer
|
||||||
from product.models import Product, ProductSubType, ProductType
|
from product.models import Product, ProductSubType, ProductType
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from location.serializers import (WineRegionBaseSerializer, WineAppellationBaseSerializer,
|
from location.serializers import WineRegionBaseSerializer, CountrySimpleSerializer
|
||||||
CountrySimpleSerializer)
|
from gallery.models import Image
|
||||||
|
|
||||||
|
|
||||||
class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
|
class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -44,7 +44,6 @@ class ProductBaseSerializer(serializers.ModelSerializer):
|
||||||
product_type = ProductTypeBaseSerializer()
|
product_type = ProductTypeBaseSerializer()
|
||||||
subtypes = ProductSubTypeBaseSerializer(many=True)
|
subtypes = ProductSubTypeBaseSerializer(many=True)
|
||||||
wine_region = WineRegionBaseSerializer(allow_null=True)
|
wine_region = WineRegionBaseSerializer(allow_null=True)
|
||||||
wine_appellation = WineAppellationBaseSerializer(allow_null=True)
|
|
||||||
available_countries = CountrySimpleSerializer(source='country', many=True)
|
available_countries = CountrySimpleSerializer(source='country', many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -61,7 +60,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
|
||||||
'subtypes',
|
'subtypes',
|
||||||
'public_mark',
|
'public_mark',
|
||||||
'wine_region',
|
'wine_region',
|
||||||
'wine_appellation',
|
'standards',
|
||||||
'available_countries',
|
'available_countries',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -94,3 +93,75 @@ class ProductFavoritesCreateSerializer(FavoritesCreateSerializer):
|
||||||
'content_object': validated_data.pop('product')
|
'content_object': validated_data.pop('product')
|
||||||
})
|
})
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
|
||||||
|
# class CropImageSerializer(serializers.Serializer):
|
||||||
|
# """Serializer for crop images for News object."""
|
||||||
|
#
|
||||||
|
# preview_url = serializers.SerializerMethodField()
|
||||||
|
# promo_horizontal_web_url = serializers.SerializerMethodField()
|
||||||
|
# promo_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||||
|
# tile_horizontal_web_url = serializers.SerializerMethodField()
|
||||||
|
# tile_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||||
|
# tile_vertical_web_url = serializers.SerializerMethodField()
|
||||||
|
# highlight_vertical_web_url = serializers.SerializerMethodField()
|
||||||
|
# editor_web_url = serializers.SerializerMethodField()
|
||||||
|
# editor_mobile_url = serializers.SerializerMethodField()
|
||||||
|
#
|
||||||
|
# def get_preview_url(self, obj):
|
||||||
|
# """Get crop preview."""
|
||||||
|
# return obj.instance.get_image_url('news_preview')
|
||||||
|
#
|
||||||
|
# def get_promo_horizontal_web_url(self, obj):
|
||||||
|
# """Get crop promo_horizontal_web."""
|
||||||
|
# return obj.instance.get_image_url('news_promo_horizontal_web')
|
||||||
|
#
|
||||||
|
# def get_promo_horizontal_mobile_url(self, obj):
|
||||||
|
# """Get crop promo_horizontal_mobile."""
|
||||||
|
# return obj.instance.get_image_url('news_promo_horizontal_mobile')
|
||||||
|
#
|
||||||
|
# def get_tile_horizontal_web_url(self, obj):
|
||||||
|
# """Get crop tile_horizontal_web."""
|
||||||
|
# return obj.instance.get_image_url('news_tile_horizontal_web')
|
||||||
|
#
|
||||||
|
# def get_tile_horizontal_mobile_url(self, obj):
|
||||||
|
# """Get crop tile_horizontal_mobile."""
|
||||||
|
# return obj.instance.get_image_url('news_tile_horizontal_mobile')
|
||||||
|
#
|
||||||
|
# def get_tile_vertical_web_url(self, obj):
|
||||||
|
# """Get crop tile_vertical_web."""
|
||||||
|
# return obj.instance.get_image_url('news_tile_vertical_web')
|
||||||
|
#
|
||||||
|
# def get_highlight_vertical_web_url(self, obj):
|
||||||
|
# """Get crop highlight_vertical_web."""
|
||||||
|
# return obj.instance.get_image_url('news_highlight_vertical_web')
|
||||||
|
#
|
||||||
|
# def get_editor_web_url(self, obj):
|
||||||
|
# """Get crop editor_web."""
|
||||||
|
# return obj.instance.get_image_url('news_editor_web')
|
||||||
|
#
|
||||||
|
# def get_editor_mobile_url(self, obj):
|
||||||
|
# """Get crop editor_mobile."""
|
||||||
|
# return obj.instance.get_image_url('news_editor_mobile')
|
||||||
|
|
||||||
|
|
||||||
|
class ProductImageSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serializer for returning crop images of product image."""
|
||||||
|
|
||||||
|
orientation_display = serializers.CharField(source='get_orientation_display',
|
||||||
|
read_only=True)
|
||||||
|
original_url = serializers.URLField(source='image.url')
|
||||||
|
# auto_crop_images = CropImageSerializer(source='image', allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Image
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'orientation_display',
|
||||||
|
'original_url',
|
||||||
|
# 'auto_crop_images',
|
||||||
|
]
|
||||||
|
extra_kwargs = {
|
||||||
|
'orientation': {'write_only': True}
|
||||||
|
}
|
||||||
|
|
|
||||||
157
apps/product/transfer_data.py
Normal file
157
apps/product/transfer_data.py
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from transfer import models as transfer_models
|
||||||
|
from transfer.serializers import product as product_serializers
|
||||||
|
from transfer.serializers.partner import PartnerSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_partner():
|
||||||
|
queryset = transfer_models.EstablishmentBacklinks.objects.filter(type="Partner")
|
||||||
|
|
||||||
|
serialized_data = PartnerSerializer(data=list(queryset.values()), many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"News serializer errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_color():
|
||||||
|
queryset = transfer_models.WineColor.objects.all()
|
||||||
|
serialized_data = product_serializers.WineColorSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"transfer_wine_color errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_sugar_content():
|
||||||
|
queryset = transfer_models.WineType.objects.all()
|
||||||
|
serialized_data = product_serializers.WineTypeSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"transfer_wine_sugar_content errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_bottles_produced():
|
||||||
|
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.WineBottlesProducedSerializer(
|
||||||
|
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_classification_type():
|
||||||
|
raw_queryset = transfer_models.ProductClassification.objects.raw(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
DISTINCT name,
|
||||||
|
1 as id
|
||||||
|
FROM wine_classifications;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
queryset = [vars(query) for query in raw_queryset]
|
||||||
|
serialized_data = product_serializers.WineClassificationTypeSerializer(
|
||||||
|
data=queryset,
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"transfer_classification errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_standard():
|
||||||
|
queryset = transfer_models.ProductClassification.objects.filter(parent_id__isnull=True) \
|
||||||
|
.exclude(type='Classification')
|
||||||
|
serialized_data = product_serializers.ProductStandardSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"transfer_wine_standard errors: {serialized_data.errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_wine_classifications():
|
||||||
|
queryset = transfer_models.ProductClassification.objects.filter(type='Classification')
|
||||||
|
serialized_data = product_serializers.ProductClassificationSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
pprint(f"transfer_wine_classifications 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:
|
||||||
|
errors = []
|
||||||
|
for d in serialized_data.errors: errors.append(d) if d else None
|
||||||
|
pprint(f"transfer_product errors: {errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_plate():
|
||||||
|
queryset = transfer_models.Merchandise.objects.all()
|
||||||
|
serialized_data = product_serializers.PlateSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
errors = []
|
||||||
|
for d in serialized_data.errors: errors.append(d) if d else None
|
||||||
|
pprint(f"transfer_plates errors: {errors}")
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_plate_image():
|
||||||
|
queryset = transfer_models.Merchandise.objects.all()
|
||||||
|
serialized_data = product_serializers.PlateImageSerializer(
|
||||||
|
data=list(queryset.values()),
|
||||||
|
many=True)
|
||||||
|
if serialized_data.is_valid():
|
||||||
|
serialized_data.save()
|
||||||
|
else:
|
||||||
|
errors = []
|
||||||
|
for d in serialized_data.errors: errors.append(d) if d else None
|
||||||
|
pprint(f"transfer_plates_images errors: {errors}")
|
||||||
|
|
||||||
|
|
||||||
|
data_types = {
|
||||||
|
"partner": [transfer_partner],
|
||||||
|
"wine_characteristics": [
|
||||||
|
transfer_wine_sugar_content,
|
||||||
|
transfer_wine_color,
|
||||||
|
transfer_wine_bottles_produced,
|
||||||
|
transfer_wine_classification_type,
|
||||||
|
transfer_wine_standard,
|
||||||
|
transfer_wine_classifications,
|
||||||
|
],
|
||||||
|
"product": [
|
||||||
|
transfer_product,
|
||||||
|
transfer_plate,
|
||||||
|
transfer_plate_image,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""Product backoffice url patterns."""
|
||||||
|
from django.urls import path
|
||||||
|
from product.urls.common import urlpatterns as common_urlpatterns
|
||||||
|
from product import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('<int:pk>/gallery/', views.ProductBackOfficeGalleryListView.as_view(),
|
||||||
|
name='gallery-list'),
|
||||||
|
path('<int:pk>/gallery/<int:image_id>/', views.ProductBackOfficeGalleryCreateDestroyView.as_view(),
|
||||||
|
name='gallery-create-destroy'),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns.extend(common_urlpatterns)
|
||||||
|
|
@ -7,6 +7,7 @@ app_name = 'product'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.ProductListView.as_view(), name='list'),
|
path('', views.ProductListView.as_view(), name='list'),
|
||||||
|
path('slug/<slug:slug>', views.ProductDetailView.as_view(), name='detail'),
|
||||||
path('slug/<slug:slug>/favorites/', views.CreateFavoriteProductView.as_view(),
|
path('slug/<slug:slug>/favorites/', views.CreateFavoriteProductView.as_view(),
|
||||||
name='create-destroy-favorites')
|
name='create-destroy-favorites')
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
"""Product app back-office views."""
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db.transaction import on_commit
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from rest_framework import generics, status, permissions
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from gallery.tasks import delete_image
|
||||||
|
from product import serializers, models
|
||||||
|
|
||||||
|
|
||||||
|
class ProductBackOfficeMixinView:
|
||||||
|
"""Product back-office mixin view."""
|
||||||
|
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
queryset = models.Product.objects.with_base_related() \
|
||||||
|
.order_by('-created', )
|
||||||
|
|
||||||
|
|
||||||
|
class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView,
|
||||||
|
generics.CreateAPIView,
|
||||||
|
generics.DestroyAPIView):
|
||||||
|
"""Resource for a create gallery for product for back-office users."""
|
||||||
|
serializer_class = serializers.ProductBackOfficeGallerySerializer
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
"""
|
||||||
|
Returns the object the view is displaying.
|
||||||
|
"""
|
||||||
|
product_qs = self.filter_queryset(self.get_queryset())
|
||||||
|
|
||||||
|
product = get_object_or_404(product_qs, pk=self.kwargs['pk'])
|
||||||
|
gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs['image_id'])
|
||||||
|
|
||||||
|
# May raise a permission denied
|
||||||
|
self.check_object_permissions(self.request, gallery)
|
||||||
|
|
||||||
|
return gallery
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""Overridden create method"""
|
||||||
|
super().create(request, *args, **kwargs)
|
||||||
|
return Response(status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
"""Override destroy method."""
|
||||||
|
gallery_obj = self.get_object()
|
||||||
|
if settings.USE_CELERY:
|
||||||
|
on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id,
|
||||||
|
completely=False))
|
||||||
|
else:
|
||||||
|
on_commit(lambda: delete_image(image_id=gallery_obj.image.id,
|
||||||
|
completely=False))
|
||||||
|
# Delete an instances of ProductGallery model
|
||||||
|
gallery_obj.delete()
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.ListAPIView):
|
||||||
|
"""Resource for returning gallery for product for back-office users."""
|
||||||
|
serializer_class = serializers.ProductImageSerializer
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
"""Override get_object method."""
|
||||||
|
qs = super(ProductBackOfficeGalleryListView, self).get_queryset()
|
||||||
|
product = get_object_or_404(qs, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
# May raise a permission denied
|
||||||
|
self.check_object_permissions(self.request, product)
|
||||||
|
|
||||||
|
return product
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"""Override get_queryset method."""
|
||||||
|
return self.get_object().gallery.all()
|
||||||
|
|
@ -8,6 +8,7 @@ from product import filters
|
||||||
|
|
||||||
class ProductBaseView(generics.GenericAPIView):
|
class ProductBaseView(generics.GenericAPIView):
|
||||||
"""Product base view"""
|
"""Product base view"""
|
||||||
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Override get_queryset method."""
|
"""Override get_queryset method."""
|
||||||
|
|
@ -16,11 +17,16 @@ class ProductBaseView(generics.GenericAPIView):
|
||||||
|
|
||||||
class ProductListView(ProductBaseView, generics.ListAPIView):
|
class ProductListView(ProductBaseView, generics.ListAPIView):
|
||||||
"""List view for model Product."""
|
"""List view for model Product."""
|
||||||
permission_classes = (permissions.AllowAny, )
|
|
||||||
serializer_class = serializers.ProductBaseSerializer
|
serializer_class = serializers.ProductBaseSerializer
|
||||||
filter_class = filters.ProductFilterSet
|
filter_class = filters.ProductFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class ProductDetailView(ProductBaseView, generics.RetrieveAPIView):
|
||||||
|
"""Detail view fro model Product."""
|
||||||
|
lookup_field = 'slug'
|
||||||
|
serializer_class = serializers.ProductBaseSerializer
|
||||||
|
|
||||||
|
|
||||||
class CreateFavoriteProductView(generics.CreateAPIView,
|
class CreateFavoriteProductView(generics.CreateAPIView,
|
||||||
generics.DestroyAPIView):
|
generics.DestroyAPIView):
|
||||||
"""View for create/destroy product in favorites."""
|
"""View for create/destroy product in favorites."""
|
||||||
|
|
|
||||||
23
apps/tag/migrations/0009_auto_20191111_0731.py
Normal file
23
apps/tag/migrations/0009_auto_20191111_0731.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-11-11 07:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tag', '0008_auto_20191101_1244'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tag',
|
||||||
|
name='old_id',
|
||||||
|
field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tag',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveIntegerField(default=0, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -34,8 +34,9 @@ class Tag(TranslatedFieldsMixin, models.Model):
|
||||||
category = models.ForeignKey('TagCategory', on_delete=models.CASCADE,
|
category = models.ForeignKey('TagCategory', on_delete=models.CASCADE,
|
||||||
null=True, related_name='tags',
|
null=True, related_name='tags',
|
||||||
verbose_name=_('Category'))
|
verbose_name=_('Category'))
|
||||||
|
|
||||||
chosen_tag_settings = models.ManyToManyField(Country, through='ChosenTagSettings')
|
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()
|
objects = TagQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ class Command(BaseCommand):
|
||||||
'update_country_flag',
|
'update_country_flag',
|
||||||
'comment',
|
'comment',
|
||||||
'inquiries', # №6 - перенос запросов оценок
|
'inquiries', # №6 - перенос запросов оценок
|
||||||
|
'wine_characteristics',
|
||||||
|
'product',
|
||||||
|
'comment',
|
||||||
]
|
]
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
|
from rest_framework import serializers
|
||||||
|
from tag import models as tag_models
|
||||||
|
from django.conf import settings
|
||||||
|
from product.models import ProductType, ProductSubType
|
||||||
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
|
||||||
class SecondDbManager(models.Manager):
|
class SecondDbManager(models.Manager):
|
||||||
|
|
@ -24,3 +29,68 @@ class MigrateMixin(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class TransferSerializerMixin(serializers.ModelSerializer):
|
||||||
|
"""Mixin for transferring legacy db models."""
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
qs = self.Meta.model.objects.filter(**validated_data)
|
||||||
|
if not qs.exists():
|
||||||
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tag_category(self):
|
||||||
|
if self.CATEGORY_LABEL and self.CATEGORY_INDEX_NAME:
|
||||||
|
tag_category, _ = tag_models.TagCategory.objects.get_or_create(
|
||||||
|
index_name=self.CATEGORY_INDEX_NAME,
|
||||||
|
defaults={
|
||||||
|
'label': {settings.FALLBACK_LOCALE: self.CATEGORY_LABEL},
|
||||||
|
'value_type': tag_models.TagCategory.STRING,
|
||||||
|
'index_name': self.CATEGORY_INDEX_NAME,
|
||||||
|
'public': True
|
||||||
|
})
|
||||||
|
return tag_category
|
||||||
|
|
||||||
|
def get_vintage_year(self, vintage):
|
||||||
|
earliest_year = self.Meta.model.EARLIEST_VINTAGE_YEAR
|
||||||
|
latest_year = self.Meta.model.LATEST_VINTAGE_YEAR
|
||||||
|
if vintage:
|
||||||
|
if vintage.isdigit():
|
||||||
|
if len(vintage) == 2:
|
||||||
|
if vintage == '16':
|
||||||
|
return 2016
|
||||||
|
elif len(vintage) == 4:
|
||||||
|
if earliest_year < int(vintage) < latest_year:
|
||||||
|
return int(vintage)
|
||||||
|
elif vintage == '1584':
|
||||||
|
return 1984
|
||||||
|
elif vintage == '1017':
|
||||||
|
return 2017
|
||||||
|
elif len(vintage) == 5:
|
||||||
|
if vintage == '20115':
|
||||||
|
return 2015
|
||||||
|
elif vintage == '20174':
|
||||||
|
return 2017
|
||||||
|
elif vintage.endswith('er'):
|
||||||
|
return self.get_vintage_year(vintage[:-2])
|
||||||
|
|
||||||
|
def get_product_type(self, index_name):
|
||||||
|
if index_name:
|
||||||
|
qs = ProductType.objects.filter(
|
||||||
|
index_name__icontains=index_name)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
def get_product_sub_type(self, product_type, product_sub_type):
|
||||||
|
if not isinstance(product_type, ProductType):
|
||||||
|
product_type = self.get_product_type(product_type)
|
||||||
|
if product_type and product_sub_type:
|
||||||
|
qs = ProductSubType.objects.filter(
|
||||||
|
product_type=product_type,
|
||||||
|
index_name__icontains=product_sub_type)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
def get_slug(self, name, old_id):
|
||||||
|
return slugify(f'{name}-{old_id}')
|
||||||
|
|
@ -899,47 +899,76 @@ class KeyValueMetadatumKeyValueMetadatumEstablishments(MigrateMixin):
|
||||||
db_table = 'key_value_metadatum_key_value_metadatum_establishments'
|
db_table = 'key_value_metadatum_key_value_metadatum_establishments'
|
||||||
|
|
||||||
|
|
||||||
# class Products(models.Model):
|
class WineColor(MigrateMixin):
|
||||||
# establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True)
|
using = 'legacy'
|
||||||
# brand = models.CharField(max_length=255, blank=True, null=True)
|
|
||||||
# name = models.CharField(max_length=255, blank=True, null=True)
|
name = models.CharField(max_length=255)
|
||||||
# vintage = models.CharField(max_length=255, blank=True, null=True)
|
order_number = models.IntegerField(null=True, blank=True)
|
||||||
# type = models.CharField(max_length=255, blank=True, null=True)
|
|
||||||
# unique_key = models.CharField(max_length=255, blank=True, null=True)
|
class Meta:
|
||||||
# price = models.FloatField(blank=True, null=True)
|
managed = False
|
||||||
# average_price_in_shops = models.FloatField(blank=True, null=True)
|
db_table = 'wine_colors'
|
||||||
# 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)
|
class WineType(MigrateMixin):
|
||||||
# wine_region = models.ForeignKey('WineLocations', models.DO_NOTHING, blank=True, null=True)
|
using = 'legacy'
|
||||||
# wine_type = models.ForeignKey('WineTypes', models.DO_NOTHING, blank=True, null=True)
|
|
||||||
# wine_color = models.ForeignKey('WineColors', models.DO_NOTHING, blank=True, null=True)
|
name = models.CharField(max_length=255)
|
||||||
# appellation = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
|
|
||||||
# created_at = models.DateTimeField()
|
class Meta:
|
||||||
# updated_at = models.DateTimeField()
|
managed = False
|
||||||
# state = models.CharField(max_length=255, blank=True, null=True)
|
db_table = 'wine_types'
|
||||||
# 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)
|
class ProductClassification(MigrateMixin):
|
||||||
# yard_classification = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
|
using = 'legacy'
|
||||||
# wine_quality = models.ForeignKey('WineClassifications', models.DO_NOTHING, blank=True, null=True)
|
|
||||||
# bottles_produced = models.CharField(max_length=3000, blank=True, null=True)
|
name = models.CharField(max_length=255)
|
||||||
# deu_import_id = models.IntegerField(blank=True, null=True)
|
desc = models.TextField()
|
||||||
#
|
latitude = models.FloatField(blank=True, null=True)
|
||||||
# class Meta:
|
longitude = models.FloatField(blank=True, null=True)
|
||||||
# managed = False
|
type = models.CharField(max_length=255)
|
||||||
# db_table = 'products'
|
parent_id = models.IntegerField()
|
||||||
#
|
possible_type_id = models.IntegerField(null=True, blank=True)
|
||||||
#
|
possible_color_id = models.IntegerField(null=True, blank=True)
|
||||||
# class WineTypes(models.Model):
|
fra_encima_id = models.IntegerField(null=True, blank=True)
|
||||||
# name = models.CharField(max_length=255, blank=True, null=True)
|
|
||||||
# fra_encima_id = models.IntegerField(blank=True, null=True)
|
class Meta:
|
||||||
# created_at = models.DateTimeField()
|
managed = False
|
||||||
# updated_at = models.DateTimeField()
|
db_table = 'wine_classifications'
|
||||||
#
|
|
||||||
# class Meta:
|
|
||||||
# managed = False
|
class Products(MigrateMixin):
|
||||||
# db_table = 'wine_types'
|
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('ProductClassification', 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('ProductClassification', 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('ProductClassification', models.DO_NOTHING, null=True,
|
||||||
|
related_name='product_wine_quality')
|
||||||
|
bottles_produced = models.CharField(max_length=3000, null=True)
|
||||||
|
unique_key = models.CharField(max_length=255, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
db_table = 'products'
|
||||||
|
|
||||||
|
|
||||||
class HomePages(models.Model):
|
class HomePages(models.Model):
|
||||||
using = 'legacy'
|
using = 'legacy'
|
||||||
|
|
@ -1004,6 +1033,35 @@ class Identities(MigrateMixin):
|
||||||
db_table = 'identities'
|
db_table = 'identities'
|
||||||
|
|
||||||
|
|
||||||
|
class WineLocations(MigrateMixin):
|
||||||
|
using = 'legacy'
|
||||||
|
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
desc = models.TextField(null=True)
|
||||||
|
latitude = models.FloatField(null=True)
|
||||||
|
longitude = models.FloatField(null=True)
|
||||||
|
type = models.CharField(max_length=255)
|
||||||
|
parent_id = models.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
db_table = 'wine_locations'
|
||||||
|
|
||||||
|
|
||||||
|
class Merchandise(MigrateMixin):
|
||||||
|
using = 'legacy'
|
||||||
|
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
vintage = models.CharField(max_length=255)
|
||||||
|
highlighted = models.CharField(max_length=255)
|
||||||
|
site = models.ForeignKey('Sites', models.DO_NOTHING)
|
||||||
|
attachment_suffix_url = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
db_table = 'merchandises'
|
||||||
|
|
||||||
|
|
||||||
class Inquiries(MigrateMixin):
|
class Inquiries(MigrateMixin):
|
||||||
using = 'legacy'
|
using = 'legacy'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from account.models import User
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
nickname = serializers.CharField()
|
nickname = serializers.CharField()
|
||||||
email = serializers.CharField()
|
email = serializers.CharField()
|
||||||
confirmed_at = serializers.DateTimeField()
|
confirmed_at = serializers.DateTimeField(allow_null=True)
|
||||||
id = serializers.CharField()
|
id = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -30,8 +30,11 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
# использовать get_or_create
|
# использовать get_or_create
|
||||||
User.objects.create(**validated_data)
|
User.objects.create(**validated_data)
|
||||||
|
|
||||||
def get_email_confirmed(self, obj):
|
def get_email_confirmed(self, data):
|
||||||
return True
|
if data.get("confirmed_at"):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def get_username(self, obj):
|
def get_username(self, obj):
|
||||||
return obj["email"]
|
return obj["email"]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.core.exceptions import MultipleObjectsReturned, ValidationError
|
from django.core.exceptions import MultipleObjectsReturned, ValidationError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from establishment.models import Establishment, ContactEmail, ContactPhone, EstablishmentType
|
from establishment.models import Establishment, ContactEmail, ContactPhone, EstablishmentType, \
|
||||||
|
EstablishmentSubType
|
||||||
from location.models import Address
|
from location.models import Address
|
||||||
from timetable.models import Timetable
|
from timetable.models import Timetable
|
||||||
from utils.legacy_parser import parse_legacy_schedule_content
|
from utils.legacy_parser import parse_legacy_schedule_content
|
||||||
from utils.serializers import TimeZoneChoiceField
|
from utils.serializers import TimeZoneChoiceField
|
||||||
from utils.slug_generator import generate_unique_slug
|
from utils.slug_generator import generate_unique_slug
|
||||||
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentSerializer(serializers.ModelSerializer):
|
class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -53,14 +56,16 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
old_type = data.pop('type', None)
|
||||||
|
|
||||||
data.update({
|
data.update({
|
||||||
'slug': generate_unique_slug(Establishment, data['slug'] if data['slug'] else data['name']),
|
'slug': generate_unique_slug(Establishment, data['slug'] if data['slug'] else data['name']),
|
||||||
'address_id': self.get_address(data['location']),
|
'address_id': self.get_address(data['location']),
|
||||||
'establishment_type_id': self.get_type(data),
|
'establishment_type_id': self.get_type(old_type),
|
||||||
'is_publish': data.get('state') == 'published',
|
'is_publish': data.get('state') == 'published',
|
||||||
|
'subtype': self.get_subtype(old_type),
|
||||||
})
|
})
|
||||||
data.pop('location')
|
data.pop('location')
|
||||||
data.pop('type')
|
|
||||||
data.pop('state')
|
data.pop('state')
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
@ -69,6 +74,7 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
email = validated_data.pop('email')
|
email = validated_data.pop('email')
|
||||||
phone = validated_data.pop('phone')
|
phone = validated_data.pop('phone')
|
||||||
schedules = validated_data.pop('schedules')
|
schedules = validated_data.pop('schedules')
|
||||||
|
subtypes = [validated_data.pop('subtype', None)]
|
||||||
|
|
||||||
establishment = Establishment.objects.create(**validated_data)
|
establishment = Establishment.objects.create(**validated_data)
|
||||||
if email:
|
if email:
|
||||||
|
|
@ -86,6 +92,8 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
for schedule in new_schedules:
|
for schedule in new_schedules:
|
||||||
establishment.schedule.add(schedule)
|
establishment.schedule.add(schedule)
|
||||||
establishment.save()
|
establishment.save()
|
||||||
|
if subtypes:
|
||||||
|
establishment.establishment_subtypes.add(*[i for i in subtypes if i])
|
||||||
|
|
||||||
return establishment
|
return establishment
|
||||||
|
|
||||||
|
|
@ -97,13 +105,16 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_type(data):
|
def get_type(old_type):
|
||||||
types = {
|
types = {
|
||||||
'Restaurant': EstablishmentType.RESTAURANT,
|
'Restaurant': EstablishmentType.RESTAURANT,
|
||||||
'Shop': EstablishmentType.ARTISAN,
|
'Shop': EstablishmentType.ARTISAN,
|
||||||
|
'Wineyard': EstablishmentType.PRODUCER,
|
||||||
}
|
}
|
||||||
obj, _ = EstablishmentType.objects.get_or_create(index_name=types[data['type']])
|
index_name = types.get(old_type)
|
||||||
return obj.id
|
if index_name:
|
||||||
|
obj, _ = EstablishmentType.objects.get_or_create(index_name=index_name)
|
||||||
|
return obj.id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_schedules(schedules):
|
def get_schedules(schedules):
|
||||||
|
|
@ -152,3 +163,13 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
result.append(obj)
|
result.append(obj)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_subtype(self, old_type):
|
||||||
|
if old_type == 'Wineyard':
|
||||||
|
subtype_name = 'Winery'
|
||||||
|
establishment_type_id = self.get_type(old_type)
|
||||||
|
subtype, _ = EstablishmentSubType.objects.get_or_create(
|
||||||
|
name={settings.FALLBACK_LOCALE: subtype_name},
|
||||||
|
index_name=slugify(subtype_name),
|
||||||
|
establishment_type_id=establishment_type_id)
|
||||||
|
return subtype
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
from rest_framework import serializers
|
from django.conf import settings
|
||||||
from location.models import Country, Region, City, Address
|
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from django.contrib.gis.geos import GEOSGeometry
|
from rest_framework import serializers
|
||||||
import json
|
|
||||||
|
from location import models
|
||||||
|
from transfer.mixins import TransferSerializerMixin
|
||||||
|
from utils.methods import get_point_from_coordinates
|
||||||
|
|
||||||
|
|
||||||
class CountrySerializer(serializers.ModelSerializer):
|
class CountrySerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -10,7 +12,7 @@ class CountrySerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Country
|
model = models.Country
|
||||||
fields = (
|
fields = (
|
||||||
"id",
|
"id",
|
||||||
"country_code_2",
|
"country_code_2",
|
||||||
|
|
@ -27,9 +29,9 @@ class CountrySerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Some countries already in database
|
# Some countries already in database
|
||||||
try:
|
try:
|
||||||
country = Country.objects.get(code=validated_data['code'])
|
country = models.Country.objects.get(code=validated_data['code'])
|
||||||
except Country.DoesNotExist:
|
except models.Country.DoesNotExist:
|
||||||
country = Country.objects.create(**validated_data)
|
country = models.Country.objects.create(**validated_data)
|
||||||
return country
|
return country
|
||||||
|
|
||||||
def get_country_code(self, obj):
|
def get_country_code(self, obj):
|
||||||
|
|
@ -43,7 +45,7 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Region
|
model = models.Region
|
||||||
fields = (
|
fields = (
|
||||||
"region_code",
|
"region_code",
|
||||||
"country_code_2",
|
"country_code_2",
|
||||||
|
|
@ -60,9 +62,9 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Some regions may be already in database
|
# Some regions may be already in database
|
||||||
try:
|
try:
|
||||||
region = Region.objects.get(old_id=validated_data['old_id'])
|
region = models.Region.objects.get(old_id=validated_data['old_id'])
|
||||||
except Region.DoesNotExist:
|
except models.Region.DoesNotExist:
|
||||||
region = Region.objects.create(**validated_data)
|
region = models.Region.objects.create(**validated_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"REGION ERROR: {validated_data}: {e}")
|
raise ValueError(f"REGION ERROR: {validated_data}: {e}")
|
||||||
return region
|
return region
|
||||||
|
|
@ -71,7 +73,7 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||||
print(data)
|
print(data)
|
||||||
if "subregion_code" in data and data["subregion_code"] is not None and data["subregion_code"].strip() != "":
|
if "subregion_code" in data and data["subregion_code"] is not None and data["subregion_code"].strip() != "":
|
||||||
try:
|
try:
|
||||||
parent_region = Region.objects.filter(code=str(data['region_code'])).first()
|
parent_region = models.Region.objects.filter(code=str(data['region_code'])).first()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Parent region error with {data}: {e}")
|
raise ValueError(f"Parent region error with {data}: {e}")
|
||||||
|
|
||||||
|
|
@ -86,7 +88,7 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def set_country(self, data):
|
def set_country(self, data):
|
||||||
try:
|
try:
|
||||||
country = Country.objects.get(code=data['country_code_2'])
|
country = models.Country.objects.get(code=data['country_code_2'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Country error with {data}: {e}")
|
raise ValueError(f"Country error with {data}: {e}")
|
||||||
|
|
||||||
|
|
@ -110,7 +112,7 @@ class CitySerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = City
|
model = models.City
|
||||||
fields = (
|
fields = (
|
||||||
"country_code_2",
|
"country_code_2",
|
||||||
"region_code",
|
"region_code",
|
||||||
|
|
@ -130,7 +132,7 @@ class CitySerializer(serializers.ModelSerializer):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
return City.objects.create(**validated_data)
|
return models.City.objects.create(**validated_data)
|
||||||
|
|
||||||
def set_is_island(self, data):
|
def set_is_island(self, data):
|
||||||
data['is_island'] = True if "is_island" in data \
|
data['is_island'] = True if "is_island" in data \
|
||||||
|
|
@ -145,19 +147,19 @@ class CitySerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def set_relations(self, data):
|
def set_relations(self, data):
|
||||||
try:
|
try:
|
||||||
region = Region.objects.filter(code=data['region_code']).first()
|
region = models.Region.objects.filter(code=data['region_code']).first()
|
||||||
except Region.DoesNotExist as e:
|
except models.Region.DoesNotExist as e:
|
||||||
try:
|
try:
|
||||||
region = Region.objects.filter(code=data['subregion_code']).first()
|
region = models.Region.objects.filter(code=data['subregion_code']).first()
|
||||||
except Region.DoesNotExist as e:
|
except models.Region.DoesNotExist as e:
|
||||||
raise ValueError(f"Region not found with {data}: {e}")
|
raise ValueError(f"Region not found with {data}: {e}")
|
||||||
|
|
||||||
data['region'] = region
|
data['region'] = region
|
||||||
del(data['subregion_code'])
|
del(data['subregion_code'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
country = Country.objects.get(code=data['country_code_2'])
|
country = models.Country.objects.get(code=data['country_code_2'])
|
||||||
except Country.DoesNotExist as e:
|
except models.Country.DoesNotExist as e:
|
||||||
raise ValueError(f"Country not found with {data}: {e}")
|
raise ValueError(f"Country not found with {data}: {e}")
|
||||||
|
|
||||||
data['country'] = country
|
data['country'] = country
|
||||||
|
|
@ -185,7 +187,7 @@ class AddressSerializer(serializers.ModelSerializer):
|
||||||
address = serializers.CharField()
|
address = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Address
|
model = models.Address
|
||||||
fields = (
|
fields = (
|
||||||
"id",
|
"id",
|
||||||
"city_id",
|
"city_id",
|
||||||
|
|
@ -204,7 +206,7 @@ class AddressSerializer(serializers.ModelSerializer):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
return Address.objects.create(**validated_data)
|
return models.Address.objects.create(**validated_data)
|
||||||
|
|
||||||
def set_old_id(self, data):
|
def set_old_id(self, data):
|
||||||
data['old_id'] = data.pop("id")
|
data['old_id'] = data.pop("id")
|
||||||
|
|
@ -218,8 +220,8 @@ class AddressSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def set_city(self, data):
|
def set_city(self, data):
|
||||||
try:
|
try:
|
||||||
city = City.objects.filter(old_id=data['city_id']).first()
|
city = models.City.objects.filter(old_id=data['city_id']).first()
|
||||||
except City.DoesNotExist as e:
|
except models.City.DoesNotExist as e:
|
||||||
raise ValueError(f"City not found with {data}: {e}")
|
raise ValueError(f"City not found with {data}: {e}")
|
||||||
|
|
||||||
data['city'] = city
|
data['city'] = city
|
||||||
|
|
@ -266,3 +268,84 @@ class AddressSerializer(serializers.ModelSerializer):
|
||||||
del(data['longitude'])
|
del(data['longitude'])
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class WineRegion(TransferSerializerMixin):
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
desc = serializers.CharField(allow_null=True)
|
||||||
|
latitude = serializers.FloatField(allow_null=True)
|
||||||
|
longitude = serializers.FloatField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.WineRegion
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'desc',
|
||||||
|
'latitude',
|
||||||
|
'longitude',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
latitude = attrs.pop('latitude', None)
|
||||||
|
longitude = attrs.pop('longitude', None)
|
||||||
|
|
||||||
|
attrs['old_id'] = attrs.pop('id')
|
||||||
|
attrs['description'] = {settings.FALLBACK_LOCALE: attrs.pop('desc', None)}
|
||||||
|
attrs['coordinates'] = get_point_from_coordinates(latitude, longitude)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class WineSubRegion(WineRegion):
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
parent_id = serializers.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.WineSubRegion
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'parent_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
parent_id = attrs.pop('parent_id', None)
|
||||||
|
|
||||||
|
attrs['old_id'] = attrs.pop('id')
|
||||||
|
attrs['wine_region'] = self.get_wine_region(parent_id)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def get_wine_region(self, parent_id):
|
||||||
|
qs = models.WineRegion.objects.filter(old_id=parent_id)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
|
||||||
|
class WineVillage(TransferSerializerMixin):
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
parent_id = serializers.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.WineVillage
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'parent_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
parent_id = attrs.pop('parent_id', None)
|
||||||
|
attrs['old_id'] = attrs.pop('id')
|
||||||
|
attrs['wine_region'] = self.get_wine_region(parent_id)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def get_wine_region(self, parent_id):
|
||||||
|
qs = models.WineRegion.objects.filter(old_id=parent_id)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
|
||||||
519
apps/transfer/serializers/product.py
Normal file
519
apps/transfer/serializers/product.py
Normal file
|
|
@ -0,0 +1,519 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
from product import models
|
||||||
|
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
|
||||||
|
from functools import reduce
|
||||||
|
from gallery.models import Image
|
||||||
|
|
||||||
|
|
||||||
|
class WineColorSerializer(TransferSerializerMixin):
|
||||||
|
CATEGORY_LABEL = 'Wine color'
|
||||||
|
CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL)
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField(allow_null=True)
|
||||||
|
order_number = serializers.IntegerField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = tag_models.Tag
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'order_number',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class WineTypeSerializer(TransferSerializerMixin):
|
||||||
|
CATEGORY_LABEL = 'Sugar content'
|
||||||
|
CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL)
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = tag_models.Tag
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
value = attrs.pop('name')
|
||||||
|
attrs['old_id'] = attrs.pop('id', None)
|
||||||
|
attrs['label'] = {settings.FALLBACK_LOCALE: value}
|
||||||
|
attrs['value'] = slugify(value)
|
||||||
|
attrs['category'] = self.tag_category
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class WineBottlesProducedSerializer(TransferSerializerMixin):
|
||||||
|
CATEGORY_LABEL = 'Bottles produced'
|
||||||
|
CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL)
|
||||||
|
|
||||||
|
bottles_produced = serializers.CharField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = tag_models.Tag
|
||||||
|
fields = (
|
||||||
|
'bottles_produced',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
value = attrs.pop('bottles_produced')
|
||||||
|
parsed_value = self.parsed_value(value)
|
||||||
|
attrs['label'] = {settings.FALLBACK_LOCALE: parsed_value}
|
||||||
|
attrs['value'] = slugify(parsed_value)
|
||||||
|
attrs['category'] = self.tag_category
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def parsed_value(self, value):
|
||||||
|
if value.isdigit():
|
||||||
|
return int(value)
|
||||||
|
|
||||||
|
parted = value.split(' ')
|
||||||
|
if len(parted) > 1:
|
||||||
|
values = [int(i) for i in parted if i.isdigit()]
|
||||||
|
return reduce(lambda a, b: a + b, values)
|
||||||
|
|
||||||
|
lowered = value.lower()
|
||||||
|
if 'magnum' in lowered:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
class ProductStandardSerializer(TransferSerializerMixin):
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
type = serializers.CharField(allow_null=True)
|
||||||
|
longitude = serializers.FloatField(allow_null=True)
|
||||||
|
latitude = serializers.FloatField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ProductStandard
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
'longitude',
|
||||||
|
'latitude',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
latitude = attrs.pop('latitude', None)
|
||||||
|
longitude = attrs.pop('longitude', None)
|
||||||
|
standard_type = attrs.pop('type', None)
|
||||||
|
|
||||||
|
attrs['coordinates'] = get_point_from_coordinates(latitude, longitude)
|
||||||
|
attrs['old_id'] = attrs.get('id')
|
||||||
|
attrs['standard_type'] = self.get_standard_type(standard_type)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def get_standard_type(self, type: str):
|
||||||
|
if type == 'Appellation':
|
||||||
|
return models.ProductStandard.APPELLATION
|
||||||
|
elif type == 'YardClassification':
|
||||||
|
return models.ProductStandard.WINEQUALITY
|
||||||
|
elif type == 'WineQuality':
|
||||||
|
return models.ProductStandard.YARDCLASSIFICATION
|
||||||
|
|
||||||
|
|
||||||
|
class WineClassificationTypeSerializer(TransferSerializerMixin):
|
||||||
|
|
||||||
|
name = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ProductClassificationType
|
||||||
|
fields = (
|
||||||
|
'name',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
attrs['product_type'] = self.wine_type
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wine_type(self):
|
||||||
|
qs = models.ProductType.objects.filter(index_name=models.ProductType.WINE)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
|
||||||
|
class ProductClassificationSerializer(TransferSerializerMixin):
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
possible_type_id = serializers.IntegerField(allow_null=True)
|
||||||
|
possible_color_id = serializers.IntegerField(allow_null=True)
|
||||||
|
parent_id = serializers.IntegerField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ProductClassification
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'possible_type_id',
|
||||||
|
'possible_color_id',
|
||||||
|
'parent_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
classification_name = attrs.pop('name')
|
||||||
|
possible_type_id = attrs.pop('possible_type_id', None)
|
||||||
|
possible_color_id = attrs.pop('possible_color_id', None)
|
||||||
|
parent_id = attrs.pop('parent_id', None)
|
||||||
|
|
||||||
|
attrs['old_id'] = attrs.pop('id')
|
||||||
|
attrs['classification_type'] = self.get_classification_type(classification_name)
|
||||||
|
attrs['possible_subtype_tag'] = self.get_possible_type_tag(possible_type_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)
|
||||||
|
possible_subtype_tag = validated_data.pop('possible_subtype_tag', None)
|
||||||
|
obj, _ = self.Meta.model.objects.get_or_create(**validated_data)
|
||||||
|
if possible_color_tag:
|
||||||
|
obj.tags.add(possible_color_tag)
|
||||||
|
if possible_subtype_tag:
|
||||||
|
obj.tags.add(possible_subtype_tag)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_classification_type(self, classification_name):
|
||||||
|
qs = models.ProductClassificationType.objects.filter(name__icontains=classification_name)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
def get_possible_type_tag(self, possible_type_id):
|
||||||
|
if possible_type_id:
|
||||||
|
qs = tag_models.Tag.objects.filter(
|
||||||
|
old_id=possible_type_id,
|
||||||
|
category__index_name=WineTypeSerializer.CATEGORY_INDEX_NAME)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
else:
|
||||||
|
import ipdb; ipdb.set_trace()
|
||||||
|
|
||||||
|
def get_possible_color_tag(self, possible_color_id):
|
||||||
|
if possible_color_id:
|
||||||
|
qs = tag_models.Tag.objects.filter(
|
||||||
|
old_id=possible_color_id,
|
||||||
|
category__index_name=WineColorSerializer.CATEGORY_INDEX_NAME)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
def get_wine_standard(self, parent_id):
|
||||||
|
if parent_id:
|
||||||
|
qs = models.ProductStandard.objects.filter(old_id=parent_id)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
|
||||||
|
class ProductSerializer(TransferSerializerMixin):
|
||||||
|
|
||||||
|
establishment_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=transfer_models.Establishments.objects.all(),
|
||||||
|
allow_null=True)
|
||||||
|
classification_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=transfer_models.ProductClassification.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.ProductClassification.objects.all(),
|
||||||
|
allow_null=True)
|
||||||
|
village_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=transfer_models.WineLocations.objects.all(),
|
||||||
|
allow_null=True)
|
||||||
|
# same as wine_village
|
||||||
|
# vineyard_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
# queryset=transfer_models.WineLocations.objects.all(),
|
||||||
|
# allow_null=True)
|
||||||
|
wine_quality_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=transfer_models.ProductClassification.objects.all(),
|
||||||
|
allow_null=True)
|
||||||
|
|
||||||
|
# same as establishment name
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
brand = serializers.CharField(allow_null=True, allow_blank=True)
|
||||||
|
name = serializers.CharField(allow_null=True, allow_blank=True)
|
||||||
|
vintage = serializers.CharField(allow_null=True)
|
||||||
|
type = serializers.CharField(allow_null=True)
|
||||||
|
wine_sub_region_id = serializers.CharField(allow_null=True)
|
||||||
|
state = serializers.CharField()
|
||||||
|
bottles_produced = serializers.CharField(allow_null=True, allow_blank=True)
|
||||||
|
unique_key = serializers.CharField(allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.Product
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'establishment_id', # done
|
||||||
|
'classification_id', # done
|
||||||
|
'wine_region_id', # done
|
||||||
|
'wine_type_id', # done
|
||||||
|
'wine_color_id', # done
|
||||||
|
'appellation_id', # done
|
||||||
|
'village_id', # done
|
||||||
|
# 'vineyard_id',
|
||||||
|
'wine_quality_id', # done
|
||||||
|
'brand',
|
||||||
|
'name', # done
|
||||||
|
'vintage', # done
|
||||||
|
'type', # done
|
||||||
|
'wine_sub_region_id', # done
|
||||||
|
'state', # done
|
||||||
|
'bottles_produced', # done
|
||||||
|
'unique_key', # done
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
name = attrs.pop('name', None)
|
||||||
|
establishment = attrs.pop('establishment_id', None)
|
||||||
|
brand = attrs.pop('brand', None)
|
||||||
|
classification = attrs.pop('classification_id', None)
|
||||||
|
vintage = attrs.pop('vintage', None)
|
||||||
|
product_type = attrs.pop('type')
|
||||||
|
wine_region = attrs.pop('wine_region_id', None)
|
||||||
|
wine_sub_region_id = attrs.pop('wine_sub_region_id', None)
|
||||||
|
bottles_produced = attrs.pop('bottles_produced', None)
|
||||||
|
wine_type = attrs.pop('wine_type_id', None)
|
||||||
|
wine_color = attrs.pop('wine_color_id', None)
|
||||||
|
appellation = attrs.pop('appellation_id', None)
|
||||||
|
village = attrs.pop('village_id', None)
|
||||||
|
wine_quality = attrs.pop('wine_quality_id', None)
|
||||||
|
name = self.get_name(name, brand)
|
||||||
|
old_id = attrs.pop('id')
|
||||||
|
state = self.get_state(attrs.pop('state', None))
|
||||||
|
|
||||||
|
attrs['old_id'] = old_id
|
||||||
|
attrs['name'] = name
|
||||||
|
attrs['old_unique_key'] = attrs.pop('unique_key')
|
||||||
|
attrs['establishment'] = self.get_establishment(establishment)
|
||||||
|
attrs['state'] = state
|
||||||
|
# attrs['brand'] = self.get_brand(brand)
|
||||||
|
attrs['wine_classification'] = self.get_wine_classification(classification)
|
||||||
|
attrs['wine_quality'] = self.get_wine_standard(wine_quality,
|
||||||
|
models.ProductStandard.WINEQUALITY)
|
||||||
|
attrs['wine_appellation'] = self.get_wine_standard(appellation,
|
||||||
|
models.ProductStandard.APPELLATION)
|
||||||
|
attrs['product_type'] = self.get_product_type(product_type)
|
||||||
|
attrs['vintage'] = self.get_vintage_year(vintage)
|
||||||
|
attrs['wine_region'] = self.get_wine_region(wine_region)
|
||||||
|
attrs['wine_sub_region'] = self.get_wine_sub_region(wine_sub_region_id)
|
||||||
|
attrs['bottles_produced_tag'] = self.get_tag(bottles_produced,
|
||||||
|
WineBottlesProducedSerializer.CATEGORY_INDEX_NAME)
|
||||||
|
attrs['wine_type_tag'] = self.get_tag(wine_type,
|
||||||
|
WineTypeSerializer.CATEGORY_INDEX_NAME)
|
||||||
|
attrs['wine_color_tag'] = self.get_tag(wine_color,
|
||||||
|
WineColorSerializer.CATEGORY_INDEX_NAME)
|
||||||
|
attrs['wine_village'] = self.get_wine_village(village)
|
||||||
|
attrs['available'] = self.get_availability(state)
|
||||||
|
attrs['slug'] = self.get_slug(name, old_id)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id'))
|
||||||
|
# classifications
|
||||||
|
classifications = [validated_data.pop('wine_classification', None)]
|
||||||
|
# standards
|
||||||
|
standards = [validated_data.pop('wine_quality', None),
|
||||||
|
validated_data.pop('wine_appellation', None)]
|
||||||
|
# tags
|
||||||
|
tags = [validated_data.pop('bottles_produced_tag', None),
|
||||||
|
validated_data.pop('wine_type_tag', None),
|
||||||
|
validated_data.pop('wine_color_tag', None)]
|
||||||
|
|
||||||
|
if not qs.exists():
|
||||||
|
obj = super().create(validated_data)
|
||||||
|
else:
|
||||||
|
obj = qs.first()
|
||||||
|
|
||||||
|
# adding classification
|
||||||
|
obj.classifications.add(*[i for i in classifications if i])
|
||||||
|
# adding standard
|
||||||
|
obj.standards.add(*[i for i in standards if i])
|
||||||
|
# adding tags
|
||||||
|
obj.tags.add(*[i for i in tags if i])
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_name(self, name, brand):
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
|
if brand and not name:
|
||||||
|
return brand
|
||||||
|
|
||||||
|
def get_establishment(self, establishment):
|
||||||
|
if establishment:
|
||||||
|
qs = establishment_models.Establishment.objects.filter(
|
||||||
|
old_id=establishment.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_wine_classification(self, classification):
|
||||||
|
if classification:
|
||||||
|
classification_qs = models.ProductClassification.objects.filter(
|
||||||
|
old_id=classification.id)
|
||||||
|
if classification_qs.exists():
|
||||||
|
return classification_qs.first()
|
||||||
|
|
||||||
|
def get_wine_region(self, wine_region):
|
||||||
|
if wine_region:
|
||||||
|
wine_region_qs = location_models.WineRegion.objects.filter(
|
||||||
|
old_id=wine_region.id)
|
||||||
|
if wine_region_qs.exists():
|
||||||
|
return wine_region_qs.first()
|
||||||
|
|
||||||
|
def get_wine_sub_region(self, wine_sub_region_id):
|
||||||
|
if wine_sub_region_id:
|
||||||
|
sub_region_qs = location_models.WineSubRegion.objects.filter(
|
||||||
|
old_id=wine_sub_region_id)
|
||||||
|
if sub_region_qs.exists():
|
||||||
|
return sub_region_qs.first()
|
||||||
|
|
||||||
|
def get_wine_village(self, village):
|
||||||
|
if village:
|
||||||
|
village_qs = location_models.WineVillage.objects.filter(
|
||||||
|
old_id=village.id)
|
||||||
|
if village_qs.exists():
|
||||||
|
return village_qs.first()
|
||||||
|
|
||||||
|
def get_tag(self, tag, category_index_name: str):
|
||||||
|
tag = tag.value if hasattr(tag, 'value') else tag
|
||||||
|
if tag:
|
||||||
|
qs = tag_models.Tag.objects.filter(
|
||||||
|
value=tag,
|
||||||
|
category__index_name=category_index_name)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first()
|
||||||
|
|
||||||
|
def get_wine_standard(self, standard, standard_type):
|
||||||
|
if standard:
|
||||||
|
if isinstance(standard, transfer_models.ProductClassification):
|
||||||
|
standard = standard.id
|
||||||
|
standard_qs = models.ProductStandard.objects.filter(
|
||||||
|
standard_type=standard_type,
|
||||||
|
old_id=standard)
|
||||||
|
if standard_qs.exists():
|
||||||
|
return standard_qs.first()
|
||||||
|
|
||||||
|
def get_availability(self, state: int):
|
||||||
|
if state == models.Product.PUBLISHED:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class PlateSerializer(TransferSerializerMixin):
|
||||||
|
|
||||||
|
PRODUCT_TYPE_INDEX_NAME = 'souvenir'
|
||||||
|
PRODUCT_SUB_TYPE_INDEX_NAME = 'plate'
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
vintage = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta(ProductSerializer.Meta):
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'vintage',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
old_id = attrs.pop('id')
|
||||||
|
name = attrs.get('name')
|
||||||
|
product_type = self.get_product_type(self.PRODUCT_TYPE_INDEX_NAME)
|
||||||
|
|
||||||
|
attrs['old_id'] = old_id
|
||||||
|
attrs['vintage'] = self.get_vintage_year(attrs.pop('vintage'))
|
||||||
|
attrs['product_type'] = product_type
|
||||||
|
attrs['state'] = self.Meta.model.PUBLISHED
|
||||||
|
attrs['subtype'] = self.get_product_sub_type(product_type,
|
||||||
|
self.PRODUCT_SUB_TYPE_INDEX_NAME)
|
||||||
|
attrs['slug'] = self.get_slug(name, old_id)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id'))
|
||||||
|
# subtypes
|
||||||
|
subtypes = [validated_data.pop('subtype', None)]
|
||||||
|
|
||||||
|
if not qs.exists():
|
||||||
|
obj = super().create(validated_data)
|
||||||
|
else:
|
||||||
|
obj = qs.first()
|
||||||
|
|
||||||
|
# adding classification
|
||||||
|
obj.subtypes.add(*[i for i in subtypes if i])
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
class PlateImageSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
attachment_suffix_url = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ProductGallery
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'attachment_suffix_url',
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
image = self.get_image(validated_data.pop('attachment_suffix_url', None),
|
||||||
|
validated_data.pop('name', None))
|
||||||
|
product = self.get_product(validated_data.pop('id'))
|
||||||
|
|
||||||
|
if product and image:
|
||||||
|
obj, created = models.ProductGallery.objects.get_or_create(
|
||||||
|
product=product,
|
||||||
|
image=image,
|
||||||
|
is_main=True)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_image(self, image_url, name):
|
||||||
|
if image_url:
|
||||||
|
obj, created = Image.objects.get_or_create(
|
||||||
|
title=name,
|
||||||
|
image=image_url)
|
||||||
|
return obj if created else None
|
||||||
|
|
||||||
|
def get_product(self, product_id):
|
||||||
|
if product_id:
|
||||||
|
product_qs = models.Product.objects.filter(old_id=product_id)
|
||||||
|
if product_qs.exists():
|
||||||
|
return product_qs.first()
|
||||||
|
|
@ -7,6 +7,7 @@ import string
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from django.utils.timezone import datetime
|
from django.utils.timezone import datetime
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
@ -118,3 +119,8 @@ def absolute_url_decorator(func):
|
||||||
else:
|
else:
|
||||||
return url_path
|
return url_path
|
||||||
return get_absolute_image_url
|
return get_absolute_image_url
|
||||||
|
|
||||||
|
|
||||||
|
def get_point_from_coordinates(latitude: str, longitude: str):
|
||||||
|
if latitude and longitude:
|
||||||
|
return Point(x=longitude, y=latitude, srid=4326)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- .:/code
|
- .:/code
|
||||||
- gm-mysql_db:/var/lib/mysql
|
- gm-mysql_db:/var/lib/mysql
|
||||||
networks:
|
|
||||||
- mysql_network
|
|
||||||
|
|
||||||
# PostgreSQL database
|
# PostgreSQL database
|
||||||
db:
|
db:
|
||||||
|
|
@ -31,8 +30,7 @@ services:
|
||||||
- "5436:5432"
|
- "5436:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- gm-db:/var/lib/postgresql/data/
|
- gm-db:/var/lib/postgresql/data/
|
||||||
networks:
|
|
||||||
- database_network
|
|
||||||
|
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
image: elasticsearch:7.3.1
|
image: elasticsearch:7.3.1
|
||||||
|
|
@ -46,14 +44,12 @@ services:
|
||||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
||||||
- discovery.type=single-node
|
- discovery.type=single-node
|
||||||
- xpack.security.enabled=false
|
- xpack.security.enabled=false
|
||||||
networks:
|
|
||||||
- elasticsearch_network
|
|
||||||
|
|
||||||
# Redis
|
# Redis
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: redis:alpine
|
||||||
networks:
|
|
||||||
- redis_network
|
|
||||||
|
|
||||||
# Celery
|
# Celery
|
||||||
worker:
|
worker:
|
||||||
|
|
@ -71,8 +67,7 @@ services:
|
||||||
links:
|
links:
|
||||||
- db
|
- db
|
||||||
- redis
|
- redis
|
||||||
networks:
|
|
||||||
- redis_network
|
|
||||||
|
|
||||||
worker_beat:
|
worker_beat:
|
||||||
build: .
|
build: .
|
||||||
|
|
@ -89,8 +84,7 @@ services:
|
||||||
links:
|
links:
|
||||||
- db
|
- db
|
||||||
- redis
|
- redis
|
||||||
networks:
|
|
||||||
- redis_network
|
|
||||||
|
|
||||||
# App: G&M
|
# App: G&M
|
||||||
gm_app:
|
gm_app:
|
||||||
|
|
@ -115,11 +109,7 @@ services:
|
||||||
- gm-media:/media-data
|
- gm-media:/media-data
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
networks:
|
|
||||||
- redis_network
|
|
||||||
- database_network
|
|
||||||
- mysql_network
|
|
||||||
- elasticsearch_network
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
gm-mysql_db:
|
gm-mysql_db:
|
||||||
|
|
@ -130,12 +120,3 @@ volumes:
|
||||||
name: gm-media
|
name: gm-media
|
||||||
gm-esdata:
|
gm-esdata:
|
||||||
|
|
||||||
networks:
|
|
||||||
database_network:
|
|
||||||
driver: bridge
|
|
||||||
mysql_network:
|
|
||||||
driver: bridge
|
|
||||||
elasticsearch_network:
|
|
||||||
driver: bridge
|
|
||||||
redis_network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ PROJECT_APPS = [
|
||||||
'news.apps.NewsConfig',
|
'news.apps.NewsConfig',
|
||||||
'notification.apps.NotificationConfig',
|
'notification.apps.NotificationConfig',
|
||||||
'partner.apps.PartnerConfig',
|
'partner.apps.PartnerConfig',
|
||||||
# 'product.apps.ProductConfig', Uncomment after refining task and create migrations
|
'product.apps.ProductConfig',
|
||||||
'recipe.apps.RecipeConfig',
|
'recipe.apps.RecipeConfig',
|
||||||
'search_indexes.apps.SearchIndexesConfig',
|
'search_indexes.apps.SearchIndexesConfig',
|
||||||
'translation.apps.TranslationConfig',
|
'translation.apps.TranslationConfig',
|
||||||
|
|
@ -74,9 +74,7 @@ PROJECT_APPS = [
|
||||||
'comment.apps.CommentConfig',
|
'comment.apps.CommentConfig',
|
||||||
'favorites.apps.FavoritesConfig',
|
'favorites.apps.FavoritesConfig',
|
||||||
'rating.apps.RatingConfig',
|
'rating.apps.RatingConfig',
|
||||||
'transfer.apps.TransferConfig',
|
|
||||||
'tag.apps.TagConfig',
|
'tag.apps.TagConfig',
|
||||||
'product.apps.ProductConfig',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTERNAL_APPS = [
|
EXTERNAL_APPS = [
|
||||||
|
|
@ -157,16 +155,6 @@ DATABASES = {
|
||||||
'HOST': os.environ.get('DB_HOSTNAME'),
|
'HOST': os.environ.get('DB_HOSTNAME'),
|
||||||
'PORT': os.environ.get('DB_PORT'),
|
'PORT': os.environ.get('DB_PORT'),
|
||||||
},
|
},
|
||||||
# 'legacy': {
|
|
||||||
# 'ENGINE': 'django.db.backends.mysql',
|
|
||||||
# # 'HOST': '172.17.0.1',
|
|
||||||
# # 'HOST': '172.23.0.1',
|
|
||||||
# 'HOST': 'mysql_db',
|
|
||||||
# 'PORT': 3306,
|
|
||||||
# 'NAME': 'dev',
|
|
||||||
# 'USER': 'dev',
|
|
||||||
# 'PASSWORD': 'octosecret123'
|
|
||||||
# }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,18 +38,9 @@ sentry_sdk.init(
|
||||||
integrations=[DjangoIntegration()]
|
integrations=[DjangoIntegration()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
# DATABASE
|
||||||
'default': {
|
DATABASES.update({
|
||||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
|
||||||
'NAME': os.environ.get('DB_NAME'),
|
|
||||||
'USER': os.environ.get('DB_USERNAME'),
|
|
||||||
'PASSWORD': os.environ.get('DB_PASSWORD'),
|
|
||||||
'HOST': os.environ.get('DB_HOSTNAME'),
|
|
||||||
'PORT': os.environ.get('DB_PORT'),
|
|
||||||
},
|
|
||||||
'legacy': {
|
'legacy': {
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'HOST': os.environ.get('MYSQL_HOSTNAME'),
|
'HOST': os.environ.get('MYSQL_HOSTNAME'),
|
||||||
|
|
@ -57,8 +48,12 @@ DATABASES = {
|
||||||
'NAME': os.environ.get('MYSQL_DATABASE'),
|
'NAME': os.environ.get('MYSQL_DATABASE'),
|
||||||
'USER': os.environ.get('MYSQL_USER'),
|
'USER': os.environ.get('MYSQL_USER'),
|
||||||
'PASSWORD': os.environ.get('MYSQL_PASSWORD')
|
'PASSWORD': os.environ.get('MYSQL_PASSWORD')
|
||||||
}
|
}})
|
||||||
}
|
|
||||||
|
|
||||||
|
# INSTALLED APPS
|
||||||
|
INSTALLED_APPS.append('transfer.apps.TransferConfig')
|
||||||
|
|
||||||
|
|
||||||
BROKER_URL = 'redis://localhost:6379/1'
|
BROKER_URL = 'redis://localhost:6379/1'
|
||||||
CELERY_RESULT_BACKEND = BROKER_URL
|
CELERY_RESULT_BACKEND = BROKER_URL
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,21 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION)
|
||||||
THUMBNAIL_DEBUG = True
|
THUMBNAIL_DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
|
# ADDED TRANSFER APP
|
||||||
|
INSTALLED_APPS.append('transfer.apps.TransferConfig')
|
||||||
|
|
||||||
|
|
||||||
|
# DATABASES
|
||||||
|
DATABASES.update({
|
||||||
|
'legacy': {
|
||||||
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
|
'HOST': 'mysql_db',
|
||||||
|
'PORT': 3306,
|
||||||
|
'NAME': 'dev',
|
||||||
|
'USER': 'dev',
|
||||||
|
'PASSWORD': 'octosecret123'}})
|
||||||
|
|
||||||
|
|
||||||
# LOGGING
|
# LOGGING
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
|
|
@ -73,28 +88,6 @@ LOGGING = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
|
||||||
'NAME': os.environ.get('DB_NAME'),
|
|
||||||
'USER': os.environ.get('DB_USERNAME'),
|
|
||||||
'PASSWORD': os.environ.get('DB_PASSWORD'),
|
|
||||||
'HOST': os.environ.get('DB_HOSTNAME'),
|
|
||||||
'PORT': os.environ.get('DB_PORT'),
|
|
||||||
},
|
|
||||||
'legacy': {
|
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
|
||||||
# 'HOST': '172.17.0.1',
|
|
||||||
# 'HOST': '172.23.0.1',
|
|
||||||
'HOST': 'mysql_db',
|
|
||||||
'PORT': 3306,
|
|
||||||
'NAME': 'dev',
|
|
||||||
'USER': 'dev',
|
|
||||||
'PASSWORD': 'octosecret123'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ELASTICSEARCH SETTINGS
|
# ELASTICSEARCH SETTINGS
|
||||||
ELASTICSEARCH_DSL = {
|
ELASTICSEARCH_DSL = {
|
||||||
'default': {
|
'default': {
|
||||||
|
|
@ -107,6 +100,7 @@ ELASTICSEARCH_INDEX_NAMES = {
|
||||||
'search_indexes.documents.establishment': 'local_establishment',
|
'search_indexes.documents.establishment': 'local_establishment',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TESTING = sys.argv[1:2] == ['test']
|
TESTING = sys.argv[1:2] == ['test']
|
||||||
if TESTING:
|
if TESTING:
|
||||||
ELASTICSEARCH_INDEX_NAMES = {}
|
ELASTICSEARCH_INDEX_NAMES = {}
|
||||||
|
|
|
||||||
|
|
@ -40,19 +40,6 @@ sentry_sdk.init(
|
||||||
integrations=[DjangoIntegration()]
|
integrations=[DjangoIntegration()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
|
||||||
'NAME': os.environ.get('DB_NAME'),
|
|
||||||
'USER': os.environ.get('DB_USERNAME'),
|
|
||||||
'PASSWORD': os.environ.get('DB_PASSWORD'),
|
|
||||||
'HOST': os.environ.get('DB_HOSTNAME'),
|
|
||||||
'PORT': os.environ.get('DB_PORT'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
BROKER_URL = 'redis://redis:6379/1'
|
BROKER_URL = 'redis://redis:6379/1'
|
||||||
CELERY_RESULT_BACKEND = BROKER_URL
|
CELERY_RESULT_BACKEND = BROKER_URL
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ urlpatterns = [
|
||||||
path('account/', include('account.urls.back')),
|
path('account/', include('account.urls.back')),
|
||||||
path('comment/', include('comment.urls.back')),
|
path('comment/', include('comment.urls.back')),
|
||||||
path('establishments/', include('establishment.urls.back')),
|
path('establishments/', include('establishment.urls.back')),
|
||||||
|
path('collections/', include('collection.urls.back')),
|
||||||
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
|
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
|
||||||
path('location/', include('location.urls.back')),
|
path('location/', include('location.urls.back')),
|
||||||
path('news/', include('news.urls.back')),
|
path('news/', include('news.urls.back')),
|
||||||
path('review/', include('review.urls.back')),
|
path('review/', include('review.urls.back')),
|
||||||
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
|
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
|
||||||
|
path('products/', include(('product.urls.back', 'product'), namespace='product')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ django-storages==1.7.2
|
||||||
sorl-thumbnail==12.5.0
|
sorl-thumbnail==12.5.0
|
||||||
|
|
||||||
|
|
||||||
# mysqlclient==1.4.4
|
|
||||||
PyYAML==5.1.2
|
PyYAML==5.1.2
|
||||||
|
|
||||||
# temp solution
|
# temp solution
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user