diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 09136934..62113d9b 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -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: diff --git a/apps/collection/models.py b/apps/collection/models.py index 5c9240ff..e7f02591 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -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 diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index 1cee40fc..da33d271 100644 --- a/apps/collection/serializers/common.py +++ b/apps/collection/serializers/common.py @@ -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', ] diff --git a/apps/collection/tasks.py b/apps/collection/tasks.py index c2a98608..b08a61d0 100644 --- a/apps/collection/tasks.py +++ b/apps/collection/tasks.py @@ -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( diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 54a33c5a..9f73b937 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -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: diff --git a/apps/news/tasks.py b/apps/news/tasks.py index 7ff4d504..0267fcf4 100644 --- a/apps/news/tasks.py +++ b/apps/news/tasks.py @@ -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 diff --git a/apps/news/transfer_data.py b/apps/news/transfer_data.py index c057da84..f4e16739 100644 --- a/apps/news/transfer_data.py +++ b/apps/news/transfer_data.py @@ -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() diff --git a/apps/news/urls/back.py b/apps/news/urls/back.py index 3aabeeac..54f40513 100644 --- a/apps/news/urls/back.py +++ b/apps/news/urls/back.py @@ -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('/', views.NewsBackOfficeRUDView.as_view(), - name='retrieve-update-destroy'), - path('/gallery/', views.NewsBackOfficeGalleryListView.as_view(), - name='gallery-list'), + path('/', views.NewsBackOfficeRUDView.as_view(), name='retrieve-update-destroy'), + path('/gallery/', views.NewsBackOfficeGalleryListView.as_view(), name='gallery-list'), path('/gallery//', views.NewsBackOfficeGalleryCreateDestroyView.as_view(), name='gallery-create-destroy'), path('/carousels/', views.NewsCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'), path('/clone/', views.NewsCloneView.as_view(), name='clone-news-item'), + path('search/', NewsDocumentViewSet.as_view({'get': 'list'}), name='search-news'), ] diff --git a/apps/notification/migrations/0005_auto_20191227_1212.py b/apps/notification/migrations/0005_auto_20191227_1212.py new file mode 100644 index 00000000..7304dcdc --- /dev/null +++ b/apps/notification/migrations/0005_auto_20191227_1212.py @@ -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'), + ), + ] diff --git a/apps/notification/migrations/0006_auto_20191227_1216.py b/apps/notification/migrations/0006_auto_20191227_1216.py new file mode 100644 index 00000000..61e918ef --- /dev/null +++ b/apps/notification/migrations/0006_auto_20191227_1216.py @@ -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'), + ), + ] diff --git a/apps/notification/migrations/0007_auto_20191227_1426.py b/apps/notification/migrations/0007_auto_20191227_1426.py new file mode 100644 index 00000000..503a2ac0 --- /dev/null +++ b/apps/notification/migrations/0007_auto_20191227_1426.py @@ -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'), + ), + ] diff --git a/apps/notification/migrations/0008_remove_subscribe_state.py b/apps/notification/migrations/0008_remove_subscribe_state.py new file mode 100644 index 00000000..5870e8cf --- /dev/null +++ b/apps/notification/migrations/0008_remove_subscribe_state.py @@ -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', + ), + ] diff --git a/apps/notification/migrations/0009_subscriptiontype_country.py b/apps/notification/migrations/0009_subscriptiontype_country.py new file mode 100644 index 00000000..9af323dc --- /dev/null +++ b/apps/notification/migrations/0009_subscriptiontype_country.py @@ -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'), + ), + ] diff --git a/apps/notification/migrations/0010_auto_20191231_0135.py b/apps/notification/migrations/0010_auto_20191231_0135.py new file mode 100644 index 00000000..7b83a738 --- /dev/null +++ b/apps/notification/migrations/0010_auto_20191231_0135.py @@ -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'), + ), + ] diff --git a/apps/notification/models.py b/apps/notification/models.py index a7eebbe8..2f0ba500 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -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') diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 2f15b733..4a9e64f5 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -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', + ) diff --git a/apps/notification/tests.py b/apps/notification/tests.py index 87264435..cf380410 100644 --- a/apps/notification/tests.py +++ b/apps/notification/tests.py @@ -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) diff --git a/apps/notification/urls/back.py b/apps/notification/urls/back.py new file mode 100644 index 00000000..2012ebe1 --- /dev/null +++ b/apps/notification/urls/back.py @@ -0,0 +1 @@ +urlpatterns = [] \ No newline at end of file diff --git a/apps/notification/urls/common.py b/apps/notification/urls/common.py index 0f7571f5..2ec6f152 100644 --- a/apps/notification/urls/common.py +++ b/apps/notification/urls/common.py @@ -5,7 +5,7 @@ from notification.views import common app_name = "notification" urlpatterns = [ - path('subscribe/', 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//', common.SubscribeInfoView.as_view(), name='check-code'), path('unsubscribe/', common.UnsubscribeAuthUserView.as_view(), name='unsubscribe-auth'), diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index cba9c343..f7fbf9fe 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -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 - diff --git a/apps/product/models.py b/apps/product/models.py index 2740be24..b8195fa8 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -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.""" diff --git a/apps/tag/filters.py b/apps/tag/filters.py index a731272e..28c3db33 100644 --- a/apps/tag/filters.py +++ b/apps/tag/filters.py @@ -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 diff --git a/apps/utils/export.py b/apps/utils/export.py index a72d85d6..a4dda8c6 100644 --- a/apps/utils/export.py +++ b/apps/utils/export.py @@ -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 diff --git a/project/settings/local.py b/project/settings/local.py index d5f5c404..a5c0ec8a 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -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') diff --git a/project/urls/back.py b/project/urls/back.py index e7e2b43b..8245e492 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -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')),