Merge branch 'develop' of ssh://gl.id-east.ru:222/gm/gm-backend into develop
This commit is contained in:
commit
807f8b4169
|
|
@ -8,6 +8,7 @@ from rest_framework import serializers
|
|||
from rest_framework import validators as rest_validators
|
||||
|
||||
from account import models, tasks
|
||||
from notification.models import Subscriber
|
||||
from utils import exceptions as utils_exceptions
|
||||
from utils import methods as utils_methods
|
||||
|
||||
|
|
@ -46,6 +47,12 @@ class UserSerializer(serializers.ModelSerializer):
|
|||
'newsletter',
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
user = super(UserSerializer, self).create(validated_data)
|
||||
validated_data['user'] = user
|
||||
Subscriber.objects.make_subscriber(**validated_data)
|
||||
return user
|
||||
|
||||
def validate_email(self, value):
|
||||
"""Validate email value"""
|
||||
if value == self.instance.email:
|
||||
|
|
|
|||
|
|
@ -848,10 +848,13 @@ class GuideElementManager(models.Manager):
|
|||
if product_qs.exists() and wine_region_node_qs.exists():
|
||||
wine_region_node = wine_region_node_qs.first()
|
||||
root_node = wine_region_node.get_root()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=wine_region_node,
|
||||
guide=root_node.guide,
|
||||
product=product_qs.first())
|
||||
product = product_qs.first()
|
||||
|
||||
if product.establishment:
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=wine_region_node,
|
||||
guide=root_node.guide,
|
||||
establishment=product.establishment)
|
||||
return None, False
|
||||
|
||||
def get_or_create_color_wine_section_node(self, wine_color_name: str, yard_node_id: int):
|
||||
|
|
@ -869,7 +872,7 @@ class GuideElementManager(models.Manager):
|
|||
})
|
||||
|
||||
if parent_node_qs.exists() and guide_element_type_qs.exists():
|
||||
root_node = parent_node_qs.first().get_root()
|
||||
root_node = parent_node_qs.first()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=root_node,
|
||||
wine_color_section=wine_color_section,
|
||||
|
|
@ -884,7 +887,7 @@ class GuideElementManager(models.Manager):
|
|||
review_qs = Review.objects.filter(id=review_id)
|
||||
|
||||
if parent_node_qs.exists() and wine_qs.exists() and review_qs.exists() and guide_element_type_qs.exists():
|
||||
root_node = parent_node_qs.first().get_root()
|
||||
root_node = parent_node_qs.first()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=root_node,
|
||||
product=wine_qs.first(),
|
||||
|
|
@ -973,8 +976,3 @@ class GuideElement(ProjectBaseMixin, MPTTModel):
|
|||
def __str__(self):
|
||||
"""Overridden dunder method."""
|
||||
return self.guide_element_type.name if self.guide_element_type else self.id
|
||||
|
||||
@property
|
||||
def advertorial_page(self):
|
||||
if self.advertorial:
|
||||
return self.advertorial.right_pages
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ class GuideElementBaseSerializer(serializers.ModelSerializer):
|
|||
allow_null=True)
|
||||
wine_color_section_name = serializers.CharField(source='wine_color_section.name',
|
||||
allow_null=True)
|
||||
wine_region_name = serializers.CharField(source='wine_region.name',
|
||||
allow_null=True)
|
||||
node_name = serializers.CharField(source='guide_element_type.name')
|
||||
label_photo = serializers.ImageField(source='label_photo.image', allow_null=True)
|
||||
city_name = serializers.CharField(source='city.name', allow_null=True)
|
||||
|
|
@ -167,12 +169,12 @@ class GuideElementBaseSerializer(serializers.ModelSerializer):
|
|||
'node_name',
|
||||
'establishment_detail',
|
||||
'review',
|
||||
'wine_region',
|
||||
'product_detail',
|
||||
'priority',
|
||||
'city_name',
|
||||
'section_name',
|
||||
'wine_color_section_name',
|
||||
'wine_region_name',
|
||||
'children',
|
||||
'label_photo',
|
||||
]
|
||||
|
|
@ -237,19 +239,23 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
|||
default=None)
|
||||
metadata = serializers.ListField(source='establishment.metadata',
|
||||
default=None)
|
||||
advertorial_page = serializers.IntegerField(default=None)
|
||||
advertorial = serializers.DictField(source='advertorial.__dict__', default=None)
|
||||
|
||||
# PRODUCT
|
||||
product_name = serializers.CharField(source='product.name',
|
||||
default=None)
|
||||
product_review = serializers.DictField(source='product.establishment.last_published_review_data',
|
||||
product_review = serializers.DictField(source='product.last_published_review_data',
|
||||
default=None)
|
||||
product_type = serializers.CharField(source='product.product_type_label',
|
||||
product_type = serializers.CharField(source='product.product_type.label',
|
||||
default=None)
|
||||
product_subtypes = serializers.CharField(source='product.product_subtype_labels',
|
||||
default=None)
|
||||
product_city = serializers.CharField(source='product.establishment.address.city.name',
|
||||
default=None)
|
||||
product_address = serializers.CharField(source='product.establishment.address.full_address',
|
||||
default=None)
|
||||
product_metadata = serializers.ListField(source='product.metadata',
|
||||
default=None)
|
||||
|
||||
class Meta:
|
||||
model = models.GuideElement
|
||||
|
|
@ -257,16 +263,8 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
|||
'id',
|
||||
'guide',
|
||||
'node_name',
|
||||
# 'establishment',
|
||||
# 'review',
|
||||
# 'wine_region',
|
||||
# 'product_detail',
|
||||
# 'priority',
|
||||
'city_name',
|
||||
# 'section_name',
|
||||
# 'wine_color_section_name',
|
||||
# 'children',
|
||||
'label_photo_url',
|
||||
'wine_color_section_name',
|
||||
'name',
|
||||
'public_mark',
|
||||
'toque_number',
|
||||
|
|
@ -278,5 +276,12 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
|||
'review',
|
||||
'price_level',
|
||||
'metadata',
|
||||
'advertorial_page',
|
||||
'advertorial',
|
||||
'product_name',
|
||||
'product_review',
|
||||
'product_type',
|
||||
'product_subtypes',
|
||||
'product_address',
|
||||
'product_city',
|
||||
'product_metadata',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -148,10 +148,11 @@ def export_guide(guide_id, user_id, file_type='csv'):
|
|||
guide = Guide.objects.get(id=guide_id)
|
||||
root = GuideElement.objects.get_root_node(guide)
|
||||
if root:
|
||||
nodes = root.get_descendants().select_related('review', 'establishment', 'wine_region',
|
||||
'product', 'city', 'wine_color_section',
|
||||
'section', 'label_photo', 'guide',
|
||||
'city__country', 'establishment__establishment_type')
|
||||
nodes = root.get_descendants().select_related(
|
||||
'review', 'establishment', 'wine_region',
|
||||
'product', 'city', 'wine_color_section',
|
||||
'section', 'label_photo', 'guide',
|
||||
'city__country', 'establishment__establishment_type')
|
||||
serializer = GuideElementExportSerializer(nodes, many=True)
|
||||
data = serializer.data
|
||||
SendGuideExport(
|
||||
|
|
|
|||
|
|
@ -186,16 +186,19 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
|
|||
"""Get last list actual public_mark"""
|
||||
qs = obj.establishmentemployee_set.actual().order_by('-from_date')\
|
||||
.values('establishment__public_mark').first()
|
||||
return qs['establishment__public_mark']
|
||||
return qs['establishment__public_mark'] if qs else None
|
||||
|
||||
|
||||
def get_positions(self, obj):
|
||||
"""Get last list actual positions"""
|
||||
est_id = obj.establishmentemployee_set.actual().\
|
||||
order_by('-from_date').first().establishment_id
|
||||
order_by('-from_date').first()
|
||||
|
||||
if not est_id:
|
||||
return None
|
||||
|
||||
qs = obj.establishmentemployee_set.actual()\
|
||||
.filter(establishment_id=est_id)\
|
||||
.filter(establishment_id=est_id.establishment_id)\
|
||||
.prefetch_related('position').values('position')
|
||||
|
||||
positions = models.Position.objects.filter(id__in=[q['position'] for q in qs])
|
||||
|
|
@ -205,11 +208,14 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
|
|||
def get_establishment(self, obj):
|
||||
"""Get last actual establishment"""
|
||||
est = obj.establishmentemployee_set.actual().order_by('-from_date')\
|
||||
.first().establishment
|
||||
.first()
|
||||
|
||||
if not est:
|
||||
return None
|
||||
|
||||
return {
|
||||
"id": est.id,
|
||||
"slug": est.slug
|
||||
"id": est.establishment.id,
|
||||
"slug": est.establishment.slug
|
||||
}
|
||||
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -1,43 +1,60 @@
|
|||
from datetime import datetime
|
||||
from smtplib import SMTPException
|
||||
|
||||
from celery import shared_task
|
||||
from django.core.mail import send_mail
|
||||
from notification.models import Subscriber
|
||||
from news import models
|
||||
from django.template.loader import render_to_string, get_template
|
||||
from django.conf import settings
|
||||
from smtplib import SMTPException
|
||||
from django.core.mail import send_mail
|
||||
from django.core.validators import EMPTY_VALUES
|
||||
from django.template.loader import get_template, render_to_string
|
||||
|
||||
from main.models import SiteSettings
|
||||
from news import models
|
||||
from notification.models import Subscribe
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_email_with_news(news_ids):
|
||||
subscribers = Subscriber.objects.filter(state=Subscriber.USABLE)
|
||||
subscribes = Subscribe.objects.all() \
|
||||
.prefetch_related('subscriber') \
|
||||
.prefetch_related('subscription_type')
|
||||
sent_news = models.News.objects.filter(id__in=news_ids)
|
||||
|
||||
htmly = get_template(settings.NEWS_EMAIL_TEMPLATE)
|
||||
year = datetime.now().year
|
||||
socials = list(SiteSettings.objects.with_country())
|
||||
socials = dict(zip(map(lambda s: s.country.code, socials), socials))
|
||||
for s in subscribers:
|
||||
socials_for_subscriber = socials.get(s.country_code)
|
||||
|
||||
socials = list(SiteSettings.objects.with_country().select_related('country'))
|
||||
socials = dict(zip(map(lambda social: social.country.code, socials), socials))
|
||||
|
||||
for subscribe in subscribes.filter(unsubscribe_date=None):
|
||||
country = subscribe.subscription_type.country
|
||||
|
||||
if country is None:
|
||||
continue
|
||||
|
||||
socials_for_subscriber = socials.get(country.code)
|
||||
subscriber = subscribe.subscriber
|
||||
|
||||
try:
|
||||
for n in sent_news:
|
||||
context = {"title": n.title.get(s.locale),
|
||||
"subtitle": n.subtitle.get(s.locale),
|
||||
"description": n.description.get(s.locale),
|
||||
"code": s.update_code,
|
||||
"image_url": n.image_url if n.image_url not in EMPTY_VALUES else None,
|
||||
"domain_uri": settings.DOMAIN_URI,
|
||||
"slug": n.slug,
|
||||
"country_code": s.country_code,
|
||||
"twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#',
|
||||
"instagram_page_url": socials_for_subscriber.instagram_page_url if socials_for_subscriber else '#',
|
||||
"facebook_page_url": socials_for_subscriber.facebook_page_url if socials_for_subscriber else '#',
|
||||
"send_to": s.send_to,
|
||||
"year": year}
|
||||
send_mail("G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE, context),
|
||||
settings.EMAIL_HOST_USER, [s.send_to], fail_silently=False,
|
||||
html_message=htmly.render(context))
|
||||
for new in sent_news:
|
||||
context = {
|
||||
"title": new.title.get(subscriber.locale),
|
||||
"subtitle": new.subtitle.get(subscriber.locale),
|
||||
"description": new.description.get(subscriber.locale),
|
||||
"code": subscriber.update_code,
|
||||
"image_url": new.image_url if new.image_url not in EMPTY_VALUES else None,
|
||||
"domain_uri": settings.DOMAIN_URI,
|
||||
"slug": new.slug,
|
||||
"country_code": subscriber.country_code,
|
||||
"twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#',
|
||||
"instagram_page_url": socials_for_subscriber.instagram_page_url if socials_for_subscriber else '#',
|
||||
"facebook_page_url": socials_for_subscriber.facebook_page_url if socials_for_subscriber else '#',
|
||||
"send_to": subscriber.send_to,
|
||||
"year": year
|
||||
}
|
||||
send_mail(
|
||||
"G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE, context),
|
||||
settings.EMAIL_HOST_USER, [subscriber.send_to], fail_silently=False,
|
||||
html_message=htmly.render(context)
|
||||
)
|
||||
except SMTPException:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ def add_tags():
|
|||
)
|
||||
if created:
|
||||
text_value = ' '.join(new_tag.value.split('_'))
|
||||
translation = SiteInterfaceDictionary(page={'en-GB': text_value}, keywords=f'tag.{new_tag.category}.{new_tag.value}')
|
||||
translation = SiteInterfaceDictionary(text={'en-GB': text_value}, keywords=f'tag.{new_tag.category}.{new_tag.value}')
|
||||
translation.save()
|
||||
new_tag.translation = translation
|
||||
new_tag.save()
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
from django.urls import path
|
||||
|
||||
from news import views
|
||||
from search_indexes.views import NewsDocumentViewSet
|
||||
|
||||
app_name = 'news'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.NewsBackOfficeLCView.as_view(), name='list-create'),
|
||||
path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(),
|
||||
name='retrieve-update-destroy'),
|
||||
path('<int:pk>/gallery/', views.NewsBackOfficeGalleryListView.as_view(),
|
||||
name='gallery-list'),
|
||||
path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(), name='retrieve-update-destroy'),
|
||||
path('<int:pk>/gallery/', views.NewsBackOfficeGalleryListView.as_view(), name='gallery-list'),
|
||||
path('<int:pk>/gallery/<int:image_id>/', views.NewsBackOfficeGalleryCreateDestroyView.as_view(),
|
||||
name='gallery-create-destroy'),
|
||||
path('<int:pk>/carousels/', views.NewsCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'),
|
||||
path('<int:pk>/clone/<str:country_code>', views.NewsCloneView.as_view(), name='clone-news-item'),
|
||||
path('search/', NewsDocumentViewSet.as_view({'get': 'list'}), name='search-news'),
|
||||
]
|
||||
|
|
|
|||
38
apps/notification/migrations/0005_auto_20191227_1212.py
Normal file
38
apps/notification/migrations/0005_auto_20191227_1212.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-27 12:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notification', '0004_auto_20191118_1307'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscriber',
|
||||
name='subscription_type',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Subscribe',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('subscriber', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notification.Subscriber')),
|
||||
('subscription_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notification.SubscriptionType')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Subscribe',
|
||||
'verbose_name_plural': 'Subscribes',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscriber',
|
||||
name='subscription_types',
|
||||
field=models.ManyToManyField(through='notification.Subscribe', to='notification.SubscriptionType'),
|
||||
),
|
||||
]
|
||||
24
apps/notification/migrations/0006_auto_20191227_1216.py
Normal file
24
apps/notification/migrations/0006_auto_20191227_1216.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-27 12:16
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notification', '0005_auto_20191227_1212'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscribe',
|
||||
name='subscribe_date',
|
||||
field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='Last subscribe date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscribe',
|
||||
name='unsubscribe_date',
|
||||
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Last unsubscribe date'),
|
||||
),
|
||||
]
|
||||
22
apps/notification/migrations/0007_auto_20191227_1426.py
Normal file
22
apps/notification/migrations/0007_auto_20191227_1426.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-27 14:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notification', '0006_auto_20191227_1216'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscriber',
|
||||
name='state',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscribe',
|
||||
name='state',
|
||||
field=models.PositiveIntegerField(choices=[(0, 'Unusable'), (1, 'Usable')], default=1, verbose_name='State'),
|
||||
),
|
||||
]
|
||||
17
apps/notification/migrations/0008_remove_subscribe_state.py
Normal file
17
apps/notification/migrations/0008_remove_subscribe_state.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-30 15:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notification', '0007_auto_20191227_1426'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscribe',
|
||||
name='state',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-31 01:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('location', '0033_merge_20191224_0920'),
|
||||
('notification', '0008_remove_subscribe_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscriptiontype',
|
||||
name='country',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.Country', verbose_name='Country'),
|
||||
),
|
||||
]
|
||||
19
apps/notification/migrations/0010_auto_20191231_0135.py
Normal file
19
apps/notification/migrations/0010_auto_20191231_0135.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-31 01:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notification', '0009_subscriptiontype_country'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subscriptiontype',
|
||||
name='country',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='location.Country', verbose_name='country'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
"""Notification app models."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from account.models import User
|
||||
from location.models import Country
|
||||
from utils.methods import generate_string_code
|
||||
from utils.models import ProjectBaseMixin, TranslatedFieldsMixin, TJSONField
|
||||
from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin
|
||||
|
||||
|
||||
class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin):
|
||||
|
|
@ -12,6 +16,9 @@ class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin):
|
|||
name = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('name'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
country = models.ForeignKey(Country, on_delete=models.PROTECT,
|
||||
blank=True, null=True, default=None,
|
||||
verbose_name=_('country'))
|
||||
|
||||
|
||||
# todo: associate user & subscriber after users registration
|
||||
|
|
@ -19,7 +26,7 @@ class SubscriberManager(models.Manager):
|
|||
"""Extended manager for Subscriber model."""
|
||||
|
||||
def make_subscriber(self, email=None, user=None, ip_address=None, country_code=None,
|
||||
locale=None, subscription_type=None, *args, **kwargs):
|
||||
locale=None, subscription_types=None, *args, **kwargs):
|
||||
"""Make subscriber and update info."""
|
||||
# search existing object
|
||||
if not user:
|
||||
|
|
@ -40,14 +47,15 @@ class SubscriberManager(models.Manager):
|
|||
obj.ip_address = ip_address
|
||||
obj.country_code = country_code
|
||||
obj.locale = locale
|
||||
obj.state = self.model.USABLE
|
||||
obj.update_code = generate_string_code()
|
||||
obj.subscription_type = subscription_type
|
||||
obj.save()
|
||||
obj.subscription_types.set(subscription_types)
|
||||
obj.subscribe_set.update(unsubscribe_date=None)
|
||||
else:
|
||||
obj = self.model.objects.create(user=user, email=email, ip_address=ip_address,
|
||||
country_code=country_code, locale=locale,
|
||||
subscription_type=subscription_type)
|
||||
country_code=country_code, locale=locale)
|
||||
obj.subscription_types.set(subscription_types)
|
||||
obj.subscribe_set.update(unsubscribe_date=None)
|
||||
return obj
|
||||
|
||||
def associate_user(self, user):
|
||||
|
|
@ -62,27 +70,9 @@ class SubscriberManager(models.Manager):
|
|||
return obj
|
||||
|
||||
|
||||
class SubscriberQuerySet(models.QuerySet):
|
||||
"""Extended queryset for Subscriber model."""
|
||||
|
||||
def by_usable(self, switcher=True):
|
||||
if switcher:
|
||||
return self.filter(state=self.model.USABLE)
|
||||
else:
|
||||
return self.filter(state=self.model.UNUSABLE)
|
||||
|
||||
|
||||
class Subscriber(ProjectBaseMixin):
|
||||
"""Subscriber model."""
|
||||
|
||||
UNUSABLE = 0
|
||||
USABLE = 1
|
||||
|
||||
STATE_CHOICES = (
|
||||
(UNUSABLE, _('Unusable')),
|
||||
(USABLE, _('Usable')),
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
blank=True,
|
||||
|
|
@ -96,7 +86,6 @@ class Subscriber(ProjectBaseMixin):
|
|||
ip_address = models.GenericIPAddressField(blank=True, null=True, default=None, verbose_name=_('IP address'))
|
||||
country_code = models.CharField(max_length=10, blank=True, null=True, default=None, verbose_name=_('Country code'))
|
||||
locale = models.CharField(blank=True, null=True, default=None, max_length=10, verbose_name=_('Locale identifier'))
|
||||
state = models.PositiveIntegerField(choices=STATE_CHOICES, default=USABLE, verbose_name=_('State'))
|
||||
update_code = models.CharField(
|
||||
max_length=254,
|
||||
blank=True,
|
||||
|
|
@ -107,9 +96,9 @@ class Subscriber(ProjectBaseMixin):
|
|||
)
|
||||
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||
|
||||
subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE, null=True, default=None)
|
||||
subscription_types = models.ManyToManyField(SubscriptionType, through='Subscribe')
|
||||
|
||||
objects = SubscriberManager.from_queryset(SubscriberQuerySet)()
|
||||
objects = SubscriberManager()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -123,10 +112,9 @@ class Subscriber(ProjectBaseMixin):
|
|||
self.update_code = generate_string_code()
|
||||
return super(Subscriber, self).save(*args, **kwargs)
|
||||
|
||||
def unsubscribe(self):
|
||||
def unsubscribe(self, query: dict):
|
||||
"""Unsubscribe user."""
|
||||
self.state = self.UNUSABLE
|
||||
self.save()
|
||||
self.subscribe_set.update(unsubscribe_date=now())
|
||||
|
||||
@property
|
||||
def send_to(self):
|
||||
|
|
@ -141,3 +129,19 @@ class Subscriber(ProjectBaseMixin):
|
|||
url = settings.SITE_REDIRECT_URL_UNSUBSCRIBE
|
||||
query = f'?code={self.update_code}'
|
||||
return f'{schema}://{site_domain}{url}{query}'
|
||||
|
||||
|
||||
class Subscribe(ProjectBaseMixin):
|
||||
"""Subscribe model."""
|
||||
|
||||
subscribe_date = models.DateTimeField(_('Last subscribe date'), blank=True, null=True, default=now)
|
||||
unsubscribe_date = models.DateTimeField(_('Last unsubscribe date'), blank=True, null=True, default=None)
|
||||
|
||||
subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE)
|
||||
subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
verbose_name = _('Subscribe')
|
||||
verbose_name_plural = _('Subscribes')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
"""Notification app serializers."""
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from location.serializers import CountrySimpleSerializer
|
||||
from notification import models
|
||||
from utils.methods import get_user_ip
|
||||
from utils.serializers import TranslatedField
|
||||
|
|
@ -10,6 +12,8 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer):
|
|||
"""Subscription type serializer."""
|
||||
|
||||
name_translated = TranslatedField()
|
||||
country = CountrySimpleSerializer()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
|
|
@ -18,14 +22,15 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer):
|
|||
'id',
|
||||
'index_name',
|
||||
'name_translated',
|
||||
'country',
|
||||
)
|
||||
|
||||
|
||||
class SubscribeSerializer(serializers.ModelSerializer):
|
||||
"""Subscribe serializer."""
|
||||
class CreateSubscribeSerializer(serializers.ModelSerializer):
|
||||
"""Create Subscribe serializer."""
|
||||
|
||||
email = serializers.EmailField(required=False, source='send_to')
|
||||
subscription_type = SubscriptionTypeSerializer(read_only=True)
|
||||
subscription_types = serializers.PrimaryKeyRelatedField(many=True, queryset=models.SubscriptionType.objects.all())
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -33,10 +38,9 @@ class SubscribeSerializer(serializers.ModelSerializer):
|
|||
model = models.Subscriber
|
||||
fields = (
|
||||
'email',
|
||||
'subscription_type',
|
||||
'state',
|
||||
'subscription_types',
|
||||
'link_to_unsubscribe',
|
||||
)
|
||||
read_only_fields = ('state',)
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Validate attrs."""
|
||||
|
|
@ -45,6 +49,10 @@ class SubscribeSerializer(serializers.ModelSerializer):
|
|||
|
||||
# validate email
|
||||
email = attrs.get('send_to')
|
||||
|
||||
if attrs.get('email'):
|
||||
email = attrs.get('email')
|
||||
|
||||
if user.is_authenticated:
|
||||
if email is not None and email != user.email:
|
||||
raise serializers.ValidationError(_('Does not match user email'))
|
||||
|
|
@ -57,18 +65,40 @@ class SubscribeSerializer(serializers.ModelSerializer):
|
|||
attrs['country_code'] = request.country_code
|
||||
attrs['locale'] = request.locale
|
||||
attrs['ip_address'] = get_user_ip(request)
|
||||
|
||||
if user.is_authenticated:
|
||||
attrs['user'] = user
|
||||
|
||||
subscription_type_id = self.context.get('request').parser_context.get('kwargs').get("subscription_type_pk")
|
||||
subscription_type_qs = models.SubscriptionType.objects.filter(id=subscription_type_id)
|
||||
if not subscription_type_qs.exists():
|
||||
raise serializers.ValidationError({'detail': _(f'SubscriptionType not found.')})
|
||||
attrs["subscription_type"] = subscription_type_qs.first()
|
||||
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create obj."""
|
||||
subscriber = models.Subscriber.objects.make_subscriber(**validated_data)
|
||||
return subscriber
|
||||
return models.Subscriber.objects.make_subscriber(**validated_data)
|
||||
|
||||
|
||||
class SubscribeObjectSerializer(serializers.ModelSerializer):
|
||||
"""Subscribe serializer."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Subscriber
|
||||
fields = ('subscriber', )
|
||||
read_only_fields = ('subscribe_date', 'unsubscribe_date',)
|
||||
|
||||
|
||||
class SubscribeSerializer(serializers.ModelSerializer):
|
||||
"""Subscribe serializer."""
|
||||
|
||||
email = serializers.EmailField(required=False, source='send_to')
|
||||
subscription_types = SubscriptionTypeSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Subscriber
|
||||
fields = (
|
||||
'email',
|
||||
'subscription_types',
|
||||
'link_to_unsubscribe',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
from datetime import datetime, timedelta
|
||||
from http.cookies import SimpleCookie
|
||||
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from account.models import User
|
||||
from notification.models import Subscriber
|
||||
from account.models import Role, User, UserRole
|
||||
from location.models import Country
|
||||
from main.models import SiteSettings
|
||||
from news.models import News, NewsType
|
||||
from notification.models import Subscriber, SubscriptionType
|
||||
from translation.models import Language
|
||||
|
||||
|
||||
class BaseTestCase(APITestCase):
|
||||
|
|
@ -17,23 +21,22 @@ class BaseTestCase(APITestCase):
|
|||
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
|
||||
# get tokens
|
||||
tokens = User.create_jwt_tokens(self.user)
|
||||
self.client.cookies = SimpleCookie({'access_token': tokens.get('access_token'),
|
||||
'refresh_token': tokens.get('refresh_token')})
|
||||
self.client.cookies = SimpleCookie({
|
||||
'access_token': tokens.get('access_token'),
|
||||
'refresh_token': tokens.get('refresh_token')
|
||||
})
|
||||
|
||||
|
||||
class NotificationAnonSubscribeTestCase(APITestCase):
|
||||
|
||||
def test_subscribe(self):
|
||||
|
||||
test_data = {
|
||||
"email": "test@email.com",
|
||||
"state": 1
|
||||
"email": "test@email.com"
|
||||
}
|
||||
|
||||
response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["email"], test_data["email"])
|
||||
self.assertEqual(response.json()["state"], test_data["state"])
|
||||
|
||||
|
||||
class NotificationSubscribeTestCase(BaseTestCase):
|
||||
|
|
@ -42,21 +45,17 @@ class NotificationSubscribeTestCase(BaseTestCase):
|
|||
super().setUp()
|
||||
|
||||
self.test_data = {
|
||||
"email": self.email,
|
||||
"state": 1
|
||||
"email": self.email
|
||||
}
|
||||
|
||||
def test_subscribe(self):
|
||||
|
||||
response = self.client.post("/api/web/notifications/subscribe/", data=self.test_data, format="json")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["email"], self.email)
|
||||
self.assertEqual(response.json()["state"], self.test_data["state"])
|
||||
|
||||
def test_subscribe_info_auth_user(self):
|
||||
|
||||
Subscriber.objects.create(user=self.user, email=self.email, state=1)
|
||||
Subscriber.objects.create(user=self.user, email=self.email)
|
||||
|
||||
response = self.client.get("/api/web/notifications/subscribe-info/", data=self.test_data, format="json")
|
||||
|
||||
|
|
@ -66,13 +65,12 @@ class NotificationSubscribeTestCase(BaseTestCase):
|
|||
class NotificationSubscribeInfoTestCase(APITestCase):
|
||||
|
||||
def test_subscribe_info(self):
|
||||
|
||||
self.username = 'sedragurda'
|
||||
self.password = 'sedragurdaredips19'
|
||||
self.email = 'sedragurda@desoz.com'
|
||||
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
|
||||
|
||||
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1)
|
||||
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
|
||||
|
||||
response = self.client.get(f"/api/web/notifications/subscribe-info/{test_subscriber.update_code}/")
|
||||
|
||||
|
|
@ -82,12 +80,10 @@ class NotificationSubscribeInfoTestCase(APITestCase):
|
|||
class NotificationUnsubscribeAuthUserTestCase(BaseTestCase):
|
||||
|
||||
def test_unsubscribe_auth_user(self):
|
||||
|
||||
Subscriber.objects.create(user=self.user, email=self.email, state=1)
|
||||
Subscriber.objects.create(user=self.user, email=self.email)
|
||||
|
||||
self.test_data = {
|
||||
"email": self.email,
|
||||
"state": 1
|
||||
"email": self.email
|
||||
}
|
||||
|
||||
response = self.client.patch("/api/web/notifications/unsubscribe/", data=self.test_data, format="json")
|
||||
|
|
@ -104,13 +100,102 @@ class NotificationUnsubscribeTestCase(APITestCase):
|
|||
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
|
||||
|
||||
self.test_data = {
|
||||
"email": self.email,
|
||||
"state": 1
|
||||
"email": self.email
|
||||
}
|
||||
|
||||
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1)
|
||||
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
|
||||
|
||||
response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/",
|
||||
data=self.test_data, format="json")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
class NotificationManySubscribeTestCase(APITestCase):
|
||||
|
||||
def test_subscribe(self):
|
||||
self.username = 'sedragurda'
|
||||
self.password = 'sedragurdaredips19'
|
||||
self.email = 'sedragurda@desoz.com'
|
||||
self.user = User.objects.create_user(
|
||||
username=self.username, email=self.email, password=self.password)
|
||||
|
||||
# get tokens
|
||||
tokens = User.create_jwt_tokens(self.user)
|
||||
self.client.cookies = SimpleCookie(
|
||||
{
|
||||
'access_token': tokens.get('access_token'),
|
||||
'refresh_token': tokens.get('refresh_token')
|
||||
})
|
||||
self.test_news_type = NewsType.objects.create(name="Test news type")
|
||||
|
||||
self.lang, created = Language.objects.get_or_create(
|
||||
title='Russia',
|
||||
locale='ru-RU'
|
||||
)
|
||||
|
||||
self.country_ru, created = Country.objects.get_or_create(
|
||||
name={"en-GB": "Russian"}
|
||||
)
|
||||
|
||||
self.site_ru, created = SiteSettings.objects.get_or_create(
|
||||
subdomain='ru'
|
||||
)
|
||||
|
||||
role = Role.objects.create(
|
||||
role=Role.CONTENT_PAGE_MANAGER,
|
||||
site_id=self.site_ru.id
|
||||
)
|
||||
role.save()
|
||||
|
||||
user_role = UserRole.objects.create(
|
||||
user=self.user,
|
||||
role=role
|
||||
)
|
||||
user_role.save()
|
||||
|
||||
self.test_news = News.objects.create(
|
||||
created_by=self.user, modified_by=self.user,
|
||||
title={"ru-RU": "Test news"},
|
||||
news_type=self.test_news_type,
|
||||
description={"ru-RU": "Description test news"},
|
||||
end=datetime.now() + timedelta(hours=2),
|
||||
state=News.PUBLISHED,
|
||||
slugs={'en-GB': 'test-news-slug'},
|
||||
country=self.country_ru,
|
||||
site=self.site_ru
|
||||
)
|
||||
|
||||
self.test_subscribe_type = SubscriptionType.objects.create(
|
||||
index_name='test_index_name',
|
||||
name={"ru-RU": "Test subscription type"}
|
||||
)
|
||||
|
||||
test_data = {
|
||||
'email': self.email,
|
||||
'subscription_types_pk': [
|
||||
self.test_subscribe_type.id
|
||||
]
|
||||
}
|
||||
|
||||
response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["email"], test_data["email"])
|
||||
|
||||
def test_unsubscribe(self):
|
||||
self.username = 'sedragurda'
|
||||
self.password = 'sedragurdaredips19'
|
||||
self.email = 'sedragurda@desoz.com'
|
||||
self.user = User.objects.create_user(
|
||||
username=self.username, email=self.email, password=self.password)
|
||||
|
||||
test_data = {
|
||||
"email": self.email
|
||||
}
|
||||
|
||||
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
|
||||
|
||||
response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/",
|
||||
data=test_data, format="json")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
|
|
|||
1
apps/notification/urls/back.py
Normal file
1
apps/notification/urls/back.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
urlpatterns = []
|
||||
|
|
@ -5,7 +5,7 @@ from notification.views import common
|
|||
app_name = "notification"
|
||||
|
||||
urlpatterns = [
|
||||
path('subscribe/<int:subscription_type_pk>', common.SubscribeView.as_view(), name='subscribe'),
|
||||
path('subscribe/', common.CreateSubscribeView.as_view(), name='create-subscribe'),
|
||||
path('subscribe-info/', common.SubscribeInfoAuthUserView.as_view(), name='check-code-auth'),
|
||||
path('subscribe-info/<code>/', common.SubscribeInfoView.as_view(), name='check-code'),
|
||||
path('unsubscribe/', common.UnsubscribeAuthUserView.as_view(), name='unsubscribe-auth'),
|
||||
|
|
|
|||
|
|
@ -2,22 +2,17 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
|
||||
from notification import models
|
||||
from notification.serializers import common as serializers
|
||||
|
||||
|
||||
class SubscribeView(generics.GenericAPIView):
|
||||
"""Subscribe View."""
|
||||
class CreateSubscribeView(generics.CreateAPIView):
|
||||
"""Create subscribe View."""
|
||||
|
||||
queryset = models.Subscriber.objects.all()
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response(data=serializer.data)
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = serializers.CreateSubscribeSerializer
|
||||
|
||||
|
||||
class SubscribeInfoView(generics.RetrieveAPIView):
|
||||
|
|
@ -25,7 +20,7 @@ class SubscribeInfoView(generics.RetrieveAPIView):
|
|||
|
||||
lookup_field = 'update_code'
|
||||
lookup_url_kwarg = 'code'
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
|
|
@ -33,7 +28,7 @@ class SubscribeInfoView(generics.RetrieveAPIView):
|
|||
class SubscribeInfoAuthUserView(generics.ListAPIView):
|
||||
"""Subscribe info auth user view."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
@ -42,18 +37,18 @@ class SubscribeInfoAuthUserView(generics.ListAPIView):
|
|||
return queryset.filter(user=user)
|
||||
|
||||
|
||||
class UnsubscribeView(generics.GenericAPIView):
|
||||
class UnsubscribeView(generics.UpdateAPIView):
|
||||
"""Unsubscribe view."""
|
||||
|
||||
lookup_field = 'update_code'
|
||||
lookup_url_kwarg = 'code'
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def patch(self, request, *args, **kw):
|
||||
obj = self.get_object()
|
||||
obj.unsubscribe()
|
||||
obj.unsubscribe(request.query_params)
|
||||
serializer = self.get_serializer(instance=obj)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
|
@ -61,14 +56,14 @@ class UnsubscribeView(generics.GenericAPIView):
|
|||
class UnsubscribeAuthUserView(generics.GenericAPIView):
|
||||
"""Unsubscribe auth user view."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def patch(self, request, *args, **kw):
|
||||
user = request.user
|
||||
obj = get_object_or_404(models.Subscriber, user=user)
|
||||
obj.unsubscribe()
|
||||
obj.unsubscribe(request.query_params)
|
||||
serializer = self.get_serializer(instance=obj)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
|
@ -78,4 +73,3 @@ class SubscriptionTypesView(generics.ListAPIView):
|
|||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.SubscriptionType.objects.all()
|
||||
serializer_class = serializers.SubscriptionTypeSerializer
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ class ProductType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin
|
|||
verbose_name = _('Product type')
|
||||
verbose_name_plural = _('Product types')
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return transform_into_readable_str(self.index_name)
|
||||
|
||||
|
||||
class ProductSubType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin):
|
||||
"""ProductSubtype model."""
|
||||
|
|
@ -410,6 +414,27 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
|||
if self.wine_region:
|
||||
return self.wine_region.name
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
if self.product_type:
|
||||
metadata = []
|
||||
tag_categories = (
|
||||
self.product_type.tag_categories.exclude(index_name__in=[
|
||||
'business_tag', 'purchased_item', 'accepted_payments_de',
|
||||
'accepted_payments_hr', 'drinks', 'bottles_per_year',
|
||||
'serial_number', 'surface', 'cooperative', 'tag',
|
||||
'outside_sits', 'private_room']).values_list('index_name', flat=True)
|
||||
)
|
||||
for category in tag_categories:
|
||||
tags = self.tags.filter(category__index_name=category).values_list('value', flat=True)
|
||||
|
||||
if tags.exists():
|
||||
category_tags = {category: []}
|
||||
for tag in tags:
|
||||
category_tags[category].append(tag)
|
||||
metadata.append(category_tags)
|
||||
return metadata
|
||||
|
||||
|
||||
class OnlineProductManager(ProductManager):
|
||||
"""Extended manger for OnlineProduct model."""
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ class TagsBaseFilterSet(filters.FilterSet):
|
|||
# Object type choices
|
||||
NEWS = 'news'
|
||||
ESTABLISHMENT = 'establishment'
|
||||
RECIPES = 'recipe'
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(NEWS, 'News'),
|
||||
(ESTABLISHMENT, 'Establishment'),
|
||||
(RECIPES, 'Recipe'),
|
||||
)
|
||||
|
||||
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
|
||||
|
|
@ -91,5 +93,7 @@ class TagsFilterSet(TagsBaseFilterSet):
|
|||
if self.ESTABLISHMENT in value:
|
||||
queryset = queryset.for_establishments().filter(category__value_type='list').filter(value__in=settings.ESTABLISHMENT_CHOSEN_TAGS).distinct(
|
||||
'value')
|
||||
if self.RECIPES in value:
|
||||
queryset = queryset.for_news().filter(value__in=settings.RECIPES_CHOSEN_TAGS).distinct('value')
|
||||
return queryset
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,14 @@ import csv
|
|||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
from smtplib import SMTPException
|
||||
|
||||
import docx
|
||||
import xlsxwriter
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from docx.blkcntnr import BlockItemContainer
|
||||
from timetable.models import Timetable
|
||||
from docx.shared import RGBColor, Pt
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from utils.methods import section_name_into_index_name
|
||||
|
||||
|
|
@ -83,13 +81,12 @@ class DocTemplate:
|
|||
|
||||
def template(self, data: list):
|
||||
|
||||
for instance in data:
|
||||
instance = dict(instance)
|
||||
element_id = instance.get('id')
|
||||
index_name = section_name_into_index_name(instance.get('section_name'))
|
||||
for obj in data:
|
||||
obj = dict(obj)
|
||||
index_name = section_name_into_index_name(obj.get('section_name'))
|
||||
|
||||
# ESTABLISHMENT HEADING (LEVEL 1)
|
||||
self.add_heading(name=instance['name'],
|
||||
self.add_heading(name=obj['name'],
|
||||
font_style={'size': Pt(18), 'name': 'Palatino', 'bold': False},
|
||||
level=1)
|
||||
# ESTABLISHMENT TYPE PARAGRAPH
|
||||
|
|
@ -102,13 +99,13 @@ class DocTemplate:
|
|||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# CITY NAME HEADING (LEVEL 3)
|
||||
self.add_heading(name=instance['city_name'],
|
||||
self.add_heading(name=obj['city_name'],
|
||||
font_style={'size': Pt(12), 'name': 'Arial', 'bold': True, 'italic': True},
|
||||
color_rgb=(102, 102, 102))
|
||||
self.add_empty_line()
|
||||
|
||||
# REVIEW HEADING (LEVEL 2)
|
||||
review = instance.get('review')
|
||||
review = obj.get('review')
|
||||
if review:
|
||||
self.add_heading(name='Review',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -125,7 +122,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# PHONE HEADING (LEVEL 2)
|
||||
phones = instance.get('phones')
|
||||
phones = obj.get('phones')
|
||||
if phones:
|
||||
self.add_heading(name='Phones',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -135,18 +132,18 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# ADDRESS HEADING (LEVEL 2)
|
||||
address = instance.get('address')
|
||||
address = obj.get('address')
|
||||
if address:
|
||||
self.add_heading(name='Address',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# ADDRESS DATA PARAGRAPH
|
||||
self.add_paragraph(name=instance.get('address'),
|
||||
self.add_paragraph(name=obj.get('address'),
|
||||
font_style={'size': Pt(10), 'name': 'Arial'})
|
||||
self.add_empty_line()
|
||||
|
||||
# TIMETABLE HEADING (LEVEL 2)
|
||||
schedule = instance.get('schedule')
|
||||
schedule = obj.get('schedule')
|
||||
if schedule:
|
||||
self.add_heading(name='Schedule',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -160,7 +157,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# PUBLIC MARK HEADING (LEVEL 2)
|
||||
public_mark = instance.get('public_mark')
|
||||
public_mark = obj.get('public_mark')
|
||||
if public_mark:
|
||||
self.add_heading(name='Mark',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -171,7 +168,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# TOQUE HEADING (LEVEL 2)
|
||||
toque = instance.get('toque_number')
|
||||
toque = obj.get('toque_number')
|
||||
if toque:
|
||||
self.add_heading(name='Toque',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -182,7 +179,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# TOQUE HEADING (LEVEL 2)
|
||||
price_level = instance.get('price_level')
|
||||
price_level = obj.get('price_level')
|
||||
if price_level:
|
||||
self.add_heading(name='Price level',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -193,7 +190,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# SERVICES HEADING (LEVEL 2)
|
||||
services = instance.get('services')
|
||||
services = obj.get('services')
|
||||
if services:
|
||||
self.add_heading(name='Services',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
|
|
@ -203,7 +200,7 @@ class DocTemplate:
|
|||
self.add_empty_line()
|
||||
|
||||
# METADATA HEADING (LEVEL 2)
|
||||
metadata = instance.get('metadata')
|
||||
metadata = obj.get('metadata')
|
||||
if metadata:
|
||||
for obj in metadata:
|
||||
for section, tags in obj.items():
|
||||
|
|
@ -382,60 +379,28 @@ class SendGuideExport(SendExportBase):
|
|||
name = self.guide.slug
|
||||
return f'export_{name}.{self.file_type}'
|
||||
|
||||
def make_doc_file(self):
|
||||
document = DocTemplate()
|
||||
document.template(self.get_doc_data())
|
||||
document.document.save(self.file_path)
|
||||
def get_headers(self):
|
||||
headers = list(self.get_data()[0].keys())
|
||||
headers.pop(headers.index('node_name'))
|
||||
self.success = True
|
||||
return headers
|
||||
|
||||
def get_data(self):
|
||||
return self.data
|
||||
excluded_data = ['guide', ]
|
||||
if self.guide.guide_type in [self.guide.ARTISAN, self.guide.RESTAURANT]:
|
||||
excluded_data.extend([
|
||||
'product_name',
|
||||
'product_review',
|
||||
'product_type',
|
||||
'product_subtypes',
|
||||
'product_address',
|
||||
'product_city',
|
||||
'product_metadata',
|
||||
'wine_color_section_name',
|
||||
|
||||
def get_doc_data(self):
|
||||
init_data = self.get_data()
|
||||
objects = []
|
||||
city_name = None
|
||||
section_name = None
|
||||
advertorial_page = None
|
||||
|
||||
for instance in init_data:
|
||||
row_advertorial_page = instance.get('advertorial_page')
|
||||
if row_advertorial_page:
|
||||
advertorial_page = row_advertorial_page
|
||||
else:
|
||||
instance['advertorial_page'] = advertorial_page
|
||||
|
||||
row_city = instance.get('city_name')
|
||||
if row_city:
|
||||
city_name = row_city
|
||||
else:
|
||||
instance['city_name'] = city_name
|
||||
|
||||
row_section = instance.get('node_name')
|
||||
if row_section.endswith('SectionNode'):
|
||||
section_name = row_section
|
||||
else:
|
||||
instance['section_name'] = section_name
|
||||
|
||||
if instance.pop('node_name', None) == 'EstablishmentNode':
|
||||
objects.append(instance.items())
|
||||
return objects
|
||||
|
||||
def send(self):
|
||||
self.get_file_method()
|
||||
print(f'ok: {self.file_path}')
|
||||
self.send_email()
|
||||
|
||||
def get_headers(self):
|
||||
"""Get headers for model Establishment."""
|
||||
exclude_headers = ['node_name', ]
|
||||
headers = list(self.data[0].keys())
|
||||
|
||||
if self.guide.RESTAURANT or self.guide.ARTISAN:
|
||||
exclude_headers.append('product_name', )
|
||||
if self.guide.WINE:
|
||||
exclude_headers.extend([
|
||||
'name',
|
||||
])
|
||||
elif self.guide.guide_type == self.guide.WINE:
|
||||
excluded_data.extend([
|
||||
'public_mark',
|
||||
'toque_number',
|
||||
'schedule',
|
||||
|
|
@ -446,13 +411,63 @@ class SendGuideExport(SendExportBase):
|
|||
'review',
|
||||
'price_level',
|
||||
'metadata',
|
||||
'public_mark',
|
||||
'toque_number',
|
||||
'schedule',
|
||||
'phones',
|
||||
'establishment_type',
|
||||
'establishment_subtypes',
|
||||
'city_name',
|
||||
])
|
||||
|
||||
for name in set(exclude_headers):
|
||||
headers.pop(headers.index(name))
|
||||
else:
|
||||
self.success = True
|
||||
return headers
|
||||
for obj in self.data:
|
||||
for column in excluded_data:
|
||||
obj.pop(column) if column in obj.keys() else None
|
||||
return self.data
|
||||
|
||||
def get_doc_data(self):
|
||||
init_data = self.get_data()
|
||||
objects = []
|
||||
city_name = None
|
||||
section_name = None
|
||||
ad_number_of_pages = None
|
||||
ad_right_pages = None
|
||||
|
||||
for row in init_data:
|
||||
row_advertorial = row.pop('advertorial')
|
||||
if row_advertorial:
|
||||
ad_number_of_pages = row_advertorial.get('number_of_pages')
|
||||
ad_right_pages = row_advertorial.get('right_pages')
|
||||
else:
|
||||
row['ad_number_of_pages'] = ad_number_of_pages
|
||||
row['ad_right_pages'] = ad_right_pages
|
||||
|
||||
row_city = row.get('city_name')
|
||||
if row_city:
|
||||
city_name = row_city
|
||||
else:
|
||||
row['city_name'] = city_name
|
||||
|
||||
row_section = row.get('node_name')
|
||||
if row_section.endswith('SectionNode'):
|
||||
section_name = row_section
|
||||
else:
|
||||
row['section_name'] = section_name
|
||||
|
||||
if row.pop('node_name', None) == 'EstablishmentNode':
|
||||
objects.append(row.items())
|
||||
return objects
|
||||
|
||||
def send(self):
|
||||
self.get_file_method()
|
||||
print(f'ok: {self.file_path}')
|
||||
self.send_email()
|
||||
|
||||
def make_doc_file(self):
|
||||
document = DocTemplate()
|
||||
document.template(self.get_doc_data())
|
||||
document.document.save(self.file_path)
|
||||
self.success = True
|
||||
|
||||
def make_csv_file(self):
|
||||
file_header = self.get_headers()
|
||||
|
|
@ -464,6 +479,9 @@ class SendGuideExport(SendExportBase):
|
|||
# Write headers to CSV file
|
||||
file_writer.writerow(file_header)
|
||||
city = None
|
||||
ad_number_of_pages = None
|
||||
ad_right_pages = None
|
||||
|
||||
for row in self.get_data():
|
||||
row_city = row.get('city_name')
|
||||
if row_city:
|
||||
|
|
@ -471,29 +489,110 @@ class SendGuideExport(SendExportBase):
|
|||
else:
|
||||
row['city_name'] = city
|
||||
|
||||
row_advertorial = row.get('advertorial')
|
||||
if row_advertorial:
|
||||
ad_number_of_pages = row_advertorial.get('number_of_pages')
|
||||
ad_right_pages = row_advertorial.get('right_pages')
|
||||
else:
|
||||
row['ad_number_of_pages'] = ad_number_of_pages
|
||||
row['ad_right_pages'] = ad_right_pages
|
||||
|
||||
if row.pop("node_name") == "EstablishmentNode":
|
||||
file_writer.writerow(row.values())
|
||||
|
||||
def make_xml_file(self):
|
||||
# create the file structure
|
||||
data = ET.Element('data')
|
||||
items = ET.SubElement(data, 'items')
|
||||
city = None
|
||||
for row in self.get_data():
|
||||
row_city = row.get('city_name')
|
||||
if row_city:
|
||||
city = row_city
|
||||
else:
|
||||
row['city_name'] = city
|
||||
if self.guide.guide_type == self.guide.WINE:
|
||||
# products
|
||||
city = None
|
||||
wine_color = None
|
||||
establishment_name = None
|
||||
establishment_address = None
|
||||
ad_number_of_pages = None
|
||||
ad_right_pages = None
|
||||
|
||||
if row.pop("node_name") == "EstablishmentNode":
|
||||
for key, value in row.items():
|
||||
item1 = ET.SubElement(items, 'item')
|
||||
item1.set('name', key)
|
||||
item1.text = str(value)
|
||||
# create the file structure
|
||||
data = ET.Element('data')
|
||||
products = ET.SubElement(data, 'products')
|
||||
|
||||
# create a new XML file with the results
|
||||
tree = ET.ElementTree(data)
|
||||
with open(self.file_path, 'bw') as f:
|
||||
tree.write(f)
|
||||
self.success = True
|
||||
for row in self.get_data():
|
||||
row_advertorial = row.pop('advertorial')
|
||||
if row_advertorial:
|
||||
ad_number_of_pages = row_advertorial.get('number_of_pages')
|
||||
ad_right_pages = row_advertorial.get('right_pages')
|
||||
else:
|
||||
row['ad_number_of_pages'] = ad_number_of_pages
|
||||
row['ad_right_pages'] = ad_right_pages
|
||||
|
||||
row_establishment_address = row.get('address')
|
||||
if row_establishment_address:
|
||||
establishment_address = row_establishment_address
|
||||
else:
|
||||
row['establishment_name'] = establishment_address
|
||||
|
||||
row_establishment = row.pop('name')
|
||||
if row_establishment:
|
||||
establishment_name = row_establishment
|
||||
else:
|
||||
row['establishment_name'] = establishment_name
|
||||
|
||||
row_city = row.get('product_city')
|
||||
if row_city:
|
||||
city = row_city
|
||||
else:
|
||||
row['city_name'] = city
|
||||
|
||||
row_wine_color = row.pop('wine_color_section_name')
|
||||
if row_wine_color:
|
||||
wine_color = row_wine_color
|
||||
else:
|
||||
row['wine_color'] = wine_color
|
||||
|
||||
if row.pop("node_name") == "WineNode":
|
||||
product = ET.SubElement(products, 'product')
|
||||
for key, value in row.items():
|
||||
prop = ET.SubElement(product, 'prop')
|
||||
prop.set('name', key)
|
||||
prop.text = str(value)
|
||||
|
||||
# create a new XML file with the results
|
||||
tree = ET.ElementTree(data)
|
||||
with open(self.file_path, 'bw') as f:
|
||||
tree.write(f)
|
||||
self.success = True
|
||||
|
||||
elif self.guide.guide_type in [self.guide.ARTISAN, self.guide.RESTAURANT]:
|
||||
# establishment
|
||||
# create the file structure
|
||||
data = ET.Element('data')
|
||||
items = ET.SubElement(data, 'cities')
|
||||
city = None
|
||||
ad_number_of_pages = None
|
||||
ad_right_pages = None
|
||||
|
||||
for row in self.get_data():
|
||||
row_advertorial = row.pop('advertorial')
|
||||
if row_advertorial:
|
||||
ad_number_of_pages = row_advertorial.get('number_of_pages')
|
||||
ad_right_pages = row_advertorial.get('right_pages')
|
||||
else:
|
||||
row['ad_number_of_pages'] = ad_number_of_pages
|
||||
row['ad_right_pages'] = ad_right_pages
|
||||
|
||||
row_city = row.get('city_name')
|
||||
if row_city:
|
||||
city = row_city
|
||||
else:
|
||||
row['city_name'] = city
|
||||
|
||||
if row.pop("node_name") == "EstablishmentNode":
|
||||
item = ET.SubElement(items, 'city')
|
||||
for key, value in row.items():
|
||||
prop = ET.SubElement(item, 'prop')
|
||||
prop.set('name', key)
|
||||
prop.text = str(value)
|
||||
|
||||
# create a new XML file with the results
|
||||
tree = ET.ElementTree(data)
|
||||
with open(self.file_path, 'bw') as f:
|
||||
tree.write(f)
|
||||
self.success = True
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION)
|
|||
THUMBNAIL_DEBUG = True
|
||||
|
||||
|
||||
|
||||
# DATABASES
|
||||
DATABASES = {
|
||||
'default': {
|
||||
|
|
@ -119,3 +118,6 @@ EMAIL_HOST = 'smtp.gmail.com'
|
|||
EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com'
|
||||
EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt'
|
||||
EMAIL_PORT = 587
|
||||
|
||||
# ADD TRANSFER TO INSTALLED APPS
|
||||
INSTALLED_APPS.append('transfer.apps.TransferConfig')
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ urlpatterns = [
|
|||
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
|
||||
path('location/', include('location.urls.back')),
|
||||
path('news/', include('news.urls.back')),
|
||||
path('notifications/', include(('notification.urls.back', 'notification'), namespace='notification')),
|
||||
path('review/', include('review.urls.back')),
|
||||
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
|
||||
path('products/', include(('product.urls.back', 'product'), namespace='product')),
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user