diff --git a/apps/collection/models.py b/apps/collection/models.py index 5c9240ff..f8d02a80 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(), diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index 1cee40fc..fa218920 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,26 @@ class GuideElementExportSerializer(GuideElementBaseSerializer): default=None) metadata = serializers.ListField(source='establishment.metadata', default=None) - advertorial_page = serializers.IntegerField(default=None) + # advertorial_number_of_pages = serializers.IntegerField(source='number_of_pages', + # default=None) + # advertorial_right_pages = serializers.IntegerField(source='right_pages', + # 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 +266,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 +279,13 @@ class GuideElementExportSerializer(GuideElementBaseSerializer): 'review', 'price_level', 'metadata', - 'advertorial_page', + # 'advertorial_number_of_pages', + # 'advertorial_right_pages', + '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..9535d1f8 100644 --- a/apps/collection/tasks.py +++ b/apps/collection/tasks.py @@ -151,7 +151,7 @@ def export_guide(guide_id, user_id, file_type='csv'): nodes = root.get_descendants().select_related('review', 'establishment', 'wine_region', 'product', 'city', 'wine_color_section', 'section', 'label_photo', 'guide', - 'city__country', 'establishment__establishment_type') + 'city__country', 'establishment__establishment_type')[:100] serializer = GuideElementExportSerializer(nodes, many=True) data = serializer.data SendGuideExport( 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/utils/export.py b/apps/utils/export.py index a72d85d6..97272251 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 @@ -382,13 +380,50 @@ 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.data[0].keys()) + headers.pop(headers.index('node_name')) self.success = True + return headers def get_data(self): + 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', + + ]) + elif self.guide.guide_type == self.guide.WINE: + excluded_data.extend([ + 'public_mark', + 'toque_number', + 'schedule', + 'address', + 'phones', + 'establishment_type', + 'establishment_subtypes', + 'review', + 'price_level', + 'metadata', + 'public_mark', + 'toque_number', + 'schedule', + 'phones', + 'establishment_type', + 'establishment_subtypes', + 'city_name', + ]) + + 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): @@ -426,33 +461,11 @@ class SendGuideExport(SendExportBase): 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', - 'public_mark', - 'toque_number', - 'schedule', - 'address', - 'phones', - 'establishment_type', - 'establishment_subtypes', - 'review', - 'price_level', - 'metadata', - ]) - - for name in set(exclude_headers): - headers.pop(headers.index(name)) - else: - self.success = True - return headers + 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() @@ -475,25 +488,79 @@ class SendGuideExport(SendExportBase): 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 + advertorial = 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_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 + + for row in self.get_data(): + 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