see changes
This commit is contained in:
parent
02d0112d8b
commit
b41c185a65
|
|
@ -15,7 +15,7 @@ from product.models import Product
|
|||
from review.models import Review
|
||||
from collection import tasks
|
||||
from translation.models import Language
|
||||
from utils.methods import slug_into_section_name
|
||||
from utils.methods import transform_into_readable_str
|
||||
from utils.models import (
|
||||
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
|
||||
URLImageMixin, IntermediateGalleryModelMixin
|
||||
|
|
@ -357,25 +357,23 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
|||
if settings.USE_CELERY:
|
||||
tasks.generate_establishment_guide_elements.delay(
|
||||
guide_id=self.id,
|
||||
queryset_values=self.guidefilter.filtered_queryset_values,
|
||||
section_node_name='RestaurantSectionNode' if self.RESTAURANT else self.ARTISAN
|
||||
filter_set=self.guidefilter.establishment_filter_set,
|
||||
)
|
||||
else:
|
||||
tasks.generate_establishment_guide_elements(
|
||||
guide_id=self.id,
|
||||
queryset_values=self.guidefilter.filtered_queryset_values,
|
||||
section_node_name='RestaurantSectionNode' if self.RESTAURANT else self.ARTISAN
|
||||
filter_set=self.guidefilter.establishment_filter_set,
|
||||
)
|
||||
elif self.guide_type == self.WINE:
|
||||
if settings.USE_CELERY:
|
||||
tasks.generate_product_guide_elements.delay(
|
||||
guide_id=self.id,
|
||||
queryset_values=self.guidefilter.filtered_queryset_values,
|
||||
filter_set=self.guidefilter.product_filter_set,
|
||||
)
|
||||
else:
|
||||
tasks.generate_product_guide_elements(
|
||||
guide_id=self.id,
|
||||
queryset_values=self.guidefilter.filtered_queryset_values,
|
||||
filter_set=self.guidefilter.product_filter_set,
|
||||
)
|
||||
|
||||
def regenerate_elements(self):
|
||||
|
|
@ -388,6 +386,10 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
|||
# update count elements
|
||||
self.update_count_related_objects()
|
||||
|
||||
def change_state(self, state: int):
|
||||
self.state = state
|
||||
self.save()
|
||||
|
||||
|
||||
class AdvertorialQuerySet(models.QuerySet):
|
||||
"""QuerySet for model Advertorial."""
|
||||
|
|
@ -469,7 +471,7 @@ class GuideFilter(ProjectBaseMixin):
|
|||
"""
|
||||
|
||||
value_list = []
|
||||
if hasattr(model, 'objects'):
|
||||
if hasattr(model, 'objects') and json_field:
|
||||
for value in getattr(json_field, 'get')(search_field):
|
||||
qs = model.objects.filter(**{search_field: value})
|
||||
if qs.exists():
|
||||
|
|
@ -668,17 +670,6 @@ class GuideFilter(ProjectBaseMixin):
|
|||
|
||||
return filters
|
||||
|
||||
@property
|
||||
def filtered_queryset_values(self):
|
||||
if self.guide.guide_type in [self.guide.RESTAURANT, self.guide.ARTISAN]:
|
||||
fields = Establishment._meta.get_all_field_names()
|
||||
fields.pop('tz')
|
||||
return Establishment.objects.filter(**self.establishment_filter_set) \
|
||||
.values_list()[0]
|
||||
elif self.guide.guide_type == self.guide.WINE:
|
||||
return Product.objects.filter(**self.product_filter_set) \
|
||||
.values()[0]
|
||||
|
||||
|
||||
class GuideElementType(models.Model):
|
||||
"""Model for type of guide elements."""
|
||||
|
|
@ -861,7 +852,7 @@ class GuideElementManager(models.Manager):
|
|||
parent_node_qs = GuideElement.objects.filter(id=yard_node_id)
|
||||
|
||||
if not wine_color_name.endswith('SectionNode'):
|
||||
wine_color_name = slug_into_section_name(wine_color_name)
|
||||
wine_color_name = transform_into_readable_str(wine_color_name)
|
||||
|
||||
wine_color_section, _ = GuideWineColorSection.objects.get_or_create(
|
||||
name=wine_color_name,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from rest_framework import serializers
|
|||
from rest_framework_recursive.fields import RecursiveField
|
||||
|
||||
from collection import models
|
||||
from review.serializers import ReviewBaseSerializer
|
||||
from establishment.serializers import EstablishmentGuideElementSerializer
|
||||
from location import models as location_models
|
||||
from main.serializers import SiteShortSerializer
|
||||
|
|
@ -53,24 +54,46 @@ class CollectionSerializer(CollectionBaseSerializer):
|
|||
]
|
||||
|
||||
|
||||
class GuideFilterBaseSerialzer(serializers.ModelSerializer):
|
||||
"""Serializer for model GuideFilter."""
|
||||
class GuideFilterBaseSerializer(serializers.ModelSerializer):
|
||||
"""GuideFilter serializer"""
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
model = models.GuideFilter
|
||||
fields = [
|
||||
'establishment_types',
|
||||
'locales',
|
||||
'review_states',
|
||||
'country_names',
|
||||
'region_names',
|
||||
'sub_region_names',
|
||||
'review_vintages',
|
||||
'id',
|
||||
'establishment_type_json',
|
||||
'country_json',
|
||||
'region_json',
|
||||
'sub_region_json',
|
||||
'wine_region_json',
|
||||
'with_mark',
|
||||
'locale_json',
|
||||
'max_mark',
|
||||
'min_mark',
|
||||
'with_mark',
|
||||
'review_vintage_json',
|
||||
'review_state_json',
|
||||
'guide',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'guide': {'write_only': True, 'required': False},
|
||||
'max_mark': {'required': True},
|
||||
'min_mark': {'required': True},
|
||||
}
|
||||
|
||||
@property
|
||||
def request_kwargs(self):
|
||||
"""Get url kwargs from request."""
|
||||
return self.context.get('request').parser_context.get('kwargs')
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Overridden create method."""
|
||||
guide = get_object_or_404(models.Guide.objects.all(),
|
||||
pk=self.request_kwargs.get('pk'))
|
||||
validated_data['guide'] = guide
|
||||
guide_filter = super().create(validated_data)
|
||||
guide.generate_elements()
|
||||
return guide_filter
|
||||
|
||||
|
||||
class GuideBaseSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -80,8 +103,8 @@ class GuideBaseSerializer(serializers.ModelSerializer):
|
|||
guide_type_display = serializers.CharField(read_only=True)
|
||||
site_detail = SiteShortSerializer(read_only=True,
|
||||
source='site')
|
||||
guide_filters = GuideFilterBaseSerialzer(read_only=True,
|
||||
source='guidefilter')
|
||||
guide_filters = GuideFilterBaseSerializer(read_only=True,
|
||||
source='guidefilter')
|
||||
# counters
|
||||
restaurant_counter = serializers.IntegerField(read_only=True)
|
||||
shop_counter = serializers.IntegerField(read_only=True)
|
||||
|
|
@ -121,46 +144,6 @@ class GuideBaseSerializer(serializers.ModelSerializer):
|
|||
}
|
||||
|
||||
|
||||
class GuideFilterBaseSerializer(serializers.ModelSerializer):
|
||||
"""GuideFilter serializer"""
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
model = models.GuideFilter
|
||||
fields = [
|
||||
'id',
|
||||
'establishment_type_json',
|
||||
'country_json',
|
||||
'region_json',
|
||||
'sub_region_json',
|
||||
'wine_region_json',
|
||||
'with_mark',
|
||||
'locale_json',
|
||||
'max_mark',
|
||||
'min_mark',
|
||||
'review_vintage_json',
|
||||
'review_state_json',
|
||||
'guide',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'guide': {'write_only': True, 'required': False},
|
||||
}
|
||||
|
||||
@property
|
||||
def request_kwargs(self):
|
||||
"""Get url kwargs from request."""
|
||||
return self.context.get('request').parser_context.get('kwargs')
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Overridden create method."""
|
||||
guide = get_object_or_404(models.Guide.objects.all(),
|
||||
pk=self.request_kwargs.get('pk'))
|
||||
validated_data['guide'] = guide
|
||||
guide_filter = super().create(validated_data)
|
||||
guide.generate_elements()
|
||||
return guide_filter
|
||||
|
||||
|
||||
class GuideElementBaseSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for model GuideElement."""
|
||||
establishment_detail = EstablishmentGuideElementSerializer(read_only=True,
|
||||
|
|
@ -236,8 +219,22 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
|||
"""GuideElement export serializer."""
|
||||
# establishment = EstablishmentGuideElementSerializer(read_only=True,)
|
||||
name = serializers.CharField(source='establishment.name', default=None)
|
||||
public_mark = serializers.CharField(source='establishment.public_mark', default=None)
|
||||
public_mark = serializers.CharField(source='establishment.public_mark_display', default=None)
|
||||
toque_number = serializers.CharField(source='establishment.toque_number', default=None)
|
||||
schedule = serializers.DictField(source='establishment.schedule_display',
|
||||
default=None)
|
||||
address = serializers.CharField(source='establishment.address.full_address',
|
||||
default=None)
|
||||
phones = serializers.ListField(source='establishment.contact_phones',
|
||||
default=None)
|
||||
establishment_type = serializers.CharField(source='establishment.establishment_type.label',
|
||||
default=None)
|
||||
establishment_subtypes = serializers.ListField(source='establishment.establishment_subtype_labels',
|
||||
default=None)
|
||||
review = serializers.DictField(source='establishment.last_published_review_data',
|
||||
default=None)
|
||||
price_level = serializers.CharField(source='establishment.price_level_display',
|
||||
default=None)
|
||||
|
||||
class Meta:
|
||||
model = models.GuideElement
|
||||
|
|
@ -256,6 +253,13 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
|||
# 'label_photo',
|
||||
'name',
|
||||
'public_mark',
|
||||
'toque_number'
|
||||
'toque_number',
|
||||
'schedule',
|
||||
'address',
|
||||
'phones',
|
||||
'establishment_type',
|
||||
'establishment_subtypes',
|
||||
'review',
|
||||
'price_level',
|
||||
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import logging
|
||||
|
||||
from celery import shared_task
|
||||
from utils.methods import transform_into_readable_str
|
||||
|
||||
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -28,12 +29,14 @@ def get_additional_product_data(section_node, product):
|
|||
|
||||
|
||||
@shared_task
|
||||
def generate_establishment_guide_elements(guide_id: int, queryset_values: dict, section_node_name: str):
|
||||
def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
|
||||
"""Generate guide elements."""
|
||||
from collection.models import GuideElement, Guide
|
||||
from establishment.models import Establishment
|
||||
|
||||
guide = Guide.objects.get(id=guide_id)
|
||||
guide.change_state(Guide.BUILDING)
|
||||
queryset_values = Establishment.objects.filter(**filter_set).values()
|
||||
try:
|
||||
for instance in queryset_values:
|
||||
establishment_id = instance.get('id')
|
||||
|
|
@ -47,7 +50,7 @@ def generate_establishment_guide_elements(guide_id: int, queryset_values: dict,
|
|||
if city_node:
|
||||
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
|
||||
city_node.id,
|
||||
section_node_name,
|
||||
transform_into_readable_str(establishment.establishment_type.index_name),
|
||||
)
|
||||
if section_node:
|
||||
GuideElement.objects.get_or_create_establishment_node(
|
||||
|
|
@ -67,19 +70,23 @@ def generate_establishment_guide_elements(guide_id: int, queryset_values: dict,
|
|||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
|
||||
except Exception as e:
|
||||
guide.change_state(Guide.WAITING)
|
||||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||
else:
|
||||
guide.update_count_related_objects()
|
||||
guide.change_state(Guide.BUILT)
|
||||
|
||||
|
||||
@shared_task
|
||||
def generate_product_guide_elements(guide_id: int, queryset_values: dict):
|
||||
def generate_product_guide_elements(guide_id: int, filter_set: dict):
|
||||
"""Generate guide elements."""
|
||||
from collection.models import GuideElement, Guide
|
||||
from product.models import Product
|
||||
|
||||
guide = Guide.objects.get(id=guide_id)
|
||||
guide.change_state(Guide.BUILDING)
|
||||
queryset_values = Product.objects.filter(**filter_set).values()[0]
|
||||
try:
|
||||
for instance in queryset_values:
|
||||
wine_id = instance.get('id')
|
||||
|
|
@ -123,10 +130,12 @@ def generate_product_guide_elements(guide_id: int, queryset_values: dict):
|
|||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - Product {wine_id} id is not exists.')
|
||||
except Exception as e:
|
||||
guide.change_state(Guide.WAITING)
|
||||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||
else:
|
||||
guide.update_count_related_objects()
|
||||
guide.change_state(Guide.BUILT)
|
||||
|
||||
|
||||
@shared_task
|
||||
|
|
|
|||
|
|
@ -107,9 +107,6 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
|||
class GuideListCreateView(GuideBaseView,
|
||||
generics.ListCreateAPIView):
|
||||
"""View for Guide model for BackOffice users."""
|
||||
def post(self, request, *args, **kwargs):
|
||||
super().create(request, *args, **kwargs)
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class GuideFilterCreateView(GuideFilterBaseView,
|
||||
|
|
@ -137,7 +134,7 @@ class GuideElementListView(GuideElementBaseView,
|
|||
class GuideUpdateView(GuideBaseView):
|
||||
"""View for model GuideElement for back office users."""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""POST-method to regenerate elements of guide instance."""
|
||||
guide = get_object_or_404(models.Guide.objects.all(),
|
||||
pk=self.kwargs.get('pk'))
|
||||
|
|
@ -195,3 +192,11 @@ class GuideElementExportDOCView(generics.ListAPIView):
|
|||
queryset = models.GuideElement.objects.all()
|
||||
serializer_class = serializers.GuideElementBaseSerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Overridden get_queryset method."""
|
||||
guide = get_object_or_404(
|
||||
models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
||||
tasks.export_guide(guide_id=guide.id, user_id=request.user.id, file_type='doc')
|
||||
return Response({"success": _('The file will be sent to your email.')},
|
||||
status=status.HTTP_200_OK)
|
||||
|
|
|
|||
|
|
@ -21,10 +21,12 @@ from phonenumber_field.modelfields import PhoneNumberField
|
|||
from timezone_field import TimeZoneField
|
||||
|
||||
from location.models import Address
|
||||
from timetable.models import Timetable
|
||||
from location.models import WineOriginAddressMixin
|
||||
from main.models import Award, Currency
|
||||
from review.models import Review
|
||||
from tag.models import Tag
|
||||
from utils.methods import transform_into_readable_str
|
||||
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
|
||||
TranslatedFieldsMixin, BaseAttributes, GalleryMixin,
|
||||
IntermediateGalleryModelMixin, HasTagsMixin,
|
||||
|
|
@ -61,6 +63,10 @@ class EstablishmentType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBas
|
|||
verbose_name = _('Establishment type')
|
||||
verbose_name_plural = _('Establishment types')
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return transform_into_readable_str(self.index_name)
|
||||
|
||||
|
||||
class EstablishmentSubTypeManager(models.Manager):
|
||||
"""Extended manager for establishment subtype."""
|
||||
|
|
@ -737,6 +743,65 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
|
|||
def wine_origins_unique(self):
|
||||
return self.wine_origins.distinct('wine_region')
|
||||
|
||||
@property
|
||||
def contact_phones(self):
|
||||
if self.phones:
|
||||
return [phone.as_e164 for phone in self.phones.all()]
|
||||
|
||||
@property
|
||||
def establishment_subtype_labels(self):
|
||||
if self.establishment_subtypes:
|
||||
return [transform_into_readable_str(label)
|
||||
for label in self.establishment_subtypes.all().values_list('index_name', flat=True)]
|
||||
|
||||
@property
|
||||
def schedule_display(self):
|
||||
if self.schedule:
|
||||
timetable = {}
|
||||
for weekday, closed_at, opening_at in self.schedule.all().values_list('weekday', 'closed_at', 'opening_at'):
|
||||
weekday = dict(Timetable.WEEKDAYS_CHOICES).get(weekday)
|
||||
working_hours = 'Closed'
|
||||
if closed_at and opening_at:
|
||||
working_hours = f'{str(opening_at)[:-3]} - {str(closed_at)[:-3]}'
|
||||
timetable.update({weekday: working_hours})
|
||||
return timetable
|
||||
|
||||
@property
|
||||
def price_level_display(self):
|
||||
if self.get_price_level() and self.currency:
|
||||
min_value, max_value = self.get_price_level()
|
||||
currency = self.currency.sign
|
||||
return f'From {min_value}{currency} to {max_value}{currency}'
|
||||
|
||||
@property
|
||||
def last_published_review_data(self):
|
||||
if self.last_published_review:
|
||||
return self.last_published_review.text
|
||||
|
||||
@property
|
||||
def public_mark_display(self):
|
||||
return f'{self.public_mark}/20'
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
if self.establishment_type:
|
||||
metadata = []
|
||||
tag_categories = (
|
||||
self.establishment_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']).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 EstablishmentNoteQuerySet(models.QuerySet):
|
||||
"""QuerySet for model EstablishmentNote."""
|
||||
|
|
|
|||
|
|
@ -241,6 +241,13 @@ class Address(models.Model):
|
|||
def country_id(self):
|
||||
return self.city.country_id
|
||||
|
||||
@property
|
||||
def full_address(self):
|
||||
full_address = self.get_street_name()
|
||||
if self.number and int(self.number):
|
||||
full_address = f'{self.number} {self.get_street_name()}'
|
||||
return full_address
|
||||
|
||||
|
||||
class WineRegionQuerySet(models.QuerySet):
|
||||
"""Wine region queryset."""
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class Tag(models.Model):
|
|||
verbose_name_plural = _('Tags')
|
||||
|
||||
def __str__(self):
|
||||
return f'Tag (id = {self.id}, label_translated = {self.label_translated})'
|
||||
return f'Tag (id = {self.id}, label_translated = {self.value})'
|
||||
|
||||
|
||||
class ChosenTagSettingsQuerySet(models.QuerySet):
|
||||
|
|
|
|||
|
|
@ -1,18 +1,202 @@
|
|||
import abc
|
||||
import csv
|
||||
import xlsxwriter
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from smtplib import SMTPException
|
||||
|
||||
import docx
|
||||
import xlsxwriter
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
import abc
|
||||
from docx.blkcntnr import BlockItemContainer
|
||||
from timetable.models import Timetable
|
||||
from docx.shared import RGBColor, Pt
|
||||
from utils.methods import section_name_into_index_name
|
||||
|
||||
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocTemplate:
|
||||
|
||||
DOCUMENT_FONT_NAME = 'Palatino'
|
||||
DOCUMENT_FONT_SIZE = Pt(12)
|
||||
DOCUMENT_FONT_COLOR = RGBColor(0, 0, 0)
|
||||
|
||||
def __init__(self):
|
||||
self.document = docx.Document()
|
||||
style = self.document.styles['Normal']
|
||||
style.paragraph_format.space_before = Pt(12)
|
||||
style.font.size = self.DOCUMENT_FONT_SIZE
|
||||
style.font.name = self.DOCUMENT_FONT_NAME
|
||||
style.font.color.rgb = self.DOCUMENT_FONT_COLOR
|
||||
|
||||
def add_page_break(self):
|
||||
self.document.add_page_break()
|
||||
|
||||
def add_empty_line(self):
|
||||
self.document.add_paragraph()
|
||||
self.document.add_paragraph()
|
||||
|
||||
def add_horizontal_line(self):
|
||||
return self.document.add_paragraph(f'{"_" * 50}')
|
||||
|
||||
def add_bullet_list(self, elements: (tuple, list)):
|
||||
styles = {
|
||||
'name': 'Arial',
|
||||
'size': Pt(10)
|
||||
}
|
||||
for element in elements:
|
||||
bullet = self.document.add_paragraph(style='List Bullet').add_run(element)
|
||||
self.apply_font_style(bullet, params=styles)
|
||||
|
||||
def add_heading(self, name: str, level: int = 2, font_style: dict = None,
|
||||
color_rgb: tuple = (0, 0, 0)):
|
||||
heading = self.document.add_heading(level=level).add_run(name)
|
||||
self.apply_font_style(heading, font_style)
|
||||
if color_rgb:
|
||||
self.apply_font_color(heading, color_rgb)
|
||||
|
||||
def add_paragraph(self, name: str, font_style: dict = None, color_rgb: tuple = (0, 0, 0)):
|
||||
paragraph = self.document.add_paragraph().add_run(name)
|
||||
self.apply_font_style(paragraph, font_style)
|
||||
if color_rgb:
|
||||
self.apply_font_color(paragraph, color_rgb)
|
||||
|
||||
def apply_font_style(self, section, params: dict):
|
||||
for attr, value in params.items():
|
||||
if not hasattr(section, 'font'):
|
||||
continue
|
||||
setattr(section.font, attr, value)
|
||||
|
||||
def apply_font_color(self, section, color_rgb: tuple):
|
||||
if len(color_rgb) == 3:
|
||||
font = getattr(section, 'font', None)
|
||||
color = getattr(font, 'color', None)
|
||||
if font and color:
|
||||
setattr(color, 'rgb', RGBColor(*color_rgb))
|
||||
|
||||
def template(self, data: list):
|
||||
|
||||
for instance in data:
|
||||
instance = dict(instance)
|
||||
index_name = section_name_into_index_name(instance.get('section_name'))
|
||||
|
||||
# ESTABLISHMENT HEADING (LEVEL 1)
|
||||
self.add_heading(name=instance['name'],
|
||||
font_style={'size': Pt(18), 'name': 'Palatino', 'bold': False},
|
||||
level=1)
|
||||
# ESTABLISHMENT TYPE PARAGRAPH
|
||||
self.add_paragraph(name=index_name,
|
||||
font_style={'size': Pt(8), 'name': 'Palatino', 'bold': False, 'italic': False})
|
||||
self.add_empty_line()
|
||||
|
||||
# CITY HEADING (LEVEL 2)
|
||||
self.add_heading(name='City',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# CITY NAME HEADING (LEVEL 3)
|
||||
self.add_heading(name=instance['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')
|
||||
if review:
|
||||
self.add_heading(name='Review',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
for locale, text in review.items():
|
||||
# REVIEW LOCALE HEADING (LEVEL 6)
|
||||
self.add_heading(name=locale,
|
||||
font_style={'size': Pt(11), 'name': 'Arial', 'underline': True, 'italic': True},
|
||||
color_rgb=(102, 102, 102),
|
||||
level=6)
|
||||
# REVIEW TEXT PARAGRAPH
|
||||
self.add_paragraph(name=text,
|
||||
font_style={'size': Pt(10), 'name': 'Arial'})
|
||||
|
||||
# PHONE HEADING (LEVEL 2)
|
||||
phones = instance.get('phones')
|
||||
if phones:
|
||||
self.add_heading(name='Phones',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# PHONE NUMBER PARAGRAPH
|
||||
self.add_bullet_list(phones)
|
||||
self.add_empty_line()
|
||||
|
||||
# ADDRESS HEADING (LEVEL 2)
|
||||
address = instance.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'),
|
||||
font_style={'size': Pt(10), 'name': 'Arial'})
|
||||
self.add_empty_line()
|
||||
|
||||
# TIMETABLE HEADING (LEVEL 2)
|
||||
schedule = instance.get('schedule')
|
||||
if schedule:
|
||||
self.add_heading(name='Schedule',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# TIMETABLE ITEMS PARAGRAPH
|
||||
for weekday, working_hours in schedule.items():
|
||||
bullet = self.document.add_paragraph(style='List Bullet').add_run(f'{weekday}: {working_hours}')
|
||||
self.apply_font_style(bullet, {'name': 'Arial', 'size': Pt(10)})
|
||||
self.add_empty_line()
|
||||
|
||||
# PUBLIC MARK HEADING (LEVEL 2)
|
||||
public_mark = instance.get('public_mark')
|
||||
if public_mark:
|
||||
self.add_heading(name='Mark',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
self.add_heading(name=public_mark,
|
||||
font_style={'size': Pt(10), 'name': 'Arial'},
|
||||
level=2)
|
||||
|
||||
# TOQUE HEADING (LEVEL 2)
|
||||
toque = instance.get('toque_number')
|
||||
if toque:
|
||||
self.add_heading(name='Toque',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
self.add_heading(name=toque,
|
||||
font_style={'size': Pt(10), 'name': 'Arial'},
|
||||
level=2)
|
||||
|
||||
# TOQUE HEADING (LEVEL 2)
|
||||
price_level = instance.get('price_level')
|
||||
if price_level:
|
||||
self.add_heading(name='Price level',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
self.add_heading(name=price_level,
|
||||
font_style={'size': Pt(10), 'name': 'Arial'},
|
||||
level=2)
|
||||
|
||||
# SERVICES HEADING (LEVEL 2)
|
||||
services = instance.get('services')
|
||||
if services:
|
||||
self.add_heading(name='Services',
|
||||
font_style={'size': Pt(13), 'name': 'Arial', 'bold': True},
|
||||
level=2)
|
||||
# TIMETABLE ITEMS PARAGRAPH
|
||||
self.add_bullet_list(services)
|
||||
self.add_empty_line()
|
||||
|
||||
break
|
||||
|
||||
# PAGE BREAK
|
||||
self.add_page_break()
|
||||
|
||||
|
||||
class SendExportBase:
|
||||
"""Base class of export and sending data."""
|
||||
|
||||
|
|
@ -38,8 +222,9 @@ class SendExportBase:
|
|||
@abc.abstractmethod
|
||||
def get_emails_to(self):
|
||||
return [
|
||||
'kuzmenko.da@gmail.com',
|
||||
'sinapsit@yandex.ru'
|
||||
'a.feteleu@spider.ru',
|
||||
# 'kuzmenko.da@gmail.com',
|
||||
# 'sinapsit@yandex.ru'
|
||||
]
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
@ -82,6 +267,9 @@ class SendExportBase:
|
|||
def make_xml_file(self):
|
||||
pass
|
||||
|
||||
def make_doc_file(self):
|
||||
pass
|
||||
|
||||
def send_email(self):
|
||||
|
||||
msg = EmailMultiAlternatives(
|
||||
|
|
@ -165,12 +353,18 @@ class SendGuideExport(SendExportBase):
|
|||
self.get_file_method = self.type_mapper[file_type]
|
||||
super().__init__()
|
||||
|
||||
def get_emails_to(self):
|
||||
return [self.user.email] + super().get_emails_to()
|
||||
|
||||
def get_file_name(self):
|
||||
name = self.guide.slug
|
||||
return f'export_{name}.{self.file_type}'
|
||||
|
||||
def make_doc_file(self):
|
||||
pass
|
||||
document = DocTemplate()
|
||||
document.template(self.get_doc_data())
|
||||
document.document.save(self.file_path)
|
||||
self.success = True
|
||||
|
||||
def get_headers(self):
|
||||
headers = list(self.data[0].keys())
|
||||
|
|
@ -181,6 +375,29 @@ class SendGuideExport(SendExportBase):
|
|||
def get_data(self):
|
||||
return self.data
|
||||
|
||||
def get_doc_data(self):
|
||||
init_data = self.get_data()
|
||||
objects = []
|
||||
city_name = None
|
||||
section_name = None
|
||||
|
||||
for instance in init_data:
|
||||
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}')
|
||||
|
|
@ -204,4 +421,4 @@ class SendGuideExport(SendExportBase):
|
|||
row['city_name'] = city
|
||||
|
||||
if row.pop("node_name") == "EstablishmentNode":
|
||||
file_writer.writerow(row.values())
|
||||
file_writer.writerow(row.values())
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ def dictfetchall(cursor):
|
|||
]
|
||||
|
||||
|
||||
def slug_into_section_name(slug: str, postfix: str = 'SectionNode'):
|
||||
def transform_into_readable_str(raw_string: str, postfix: str = 'SectionNode'):
|
||||
"""
|
||||
Transform slug into section name, i.e:
|
||||
like
|
||||
|
|
@ -152,6 +152,20 @@ def slug_into_section_name(slug: str, postfix: str = 'SectionNode'):
|
|||
"effervescent-rose-de-saignee"
|
||||
"""
|
||||
re_exp = r'[\w]+'
|
||||
result = re.findall(re_exp, slug)
|
||||
result = re.findall(re_exp, raw_string)
|
||||
if result:
|
||||
return f"{''.join([i.capitalize() for i in result])}{postfix}"
|
||||
|
||||
|
||||
def section_name_into_index_name(section_name: str):
|
||||
"""
|
||||
Transform slug into section name, i.e:
|
||||
like
|
||||
"Restaurant"
|
||||
from
|
||||
"RestaurantSectionNode"
|
||||
"""
|
||||
re_exp = r'[A-Z][^A-Z]*'
|
||||
result = re.findall(re_exp, section_name)
|
||||
if result:
|
||||
return f"{' '.join([word.capitalize() if i == 0 else word for i, word in enumerate(result[:-2])])}"
|
||||
|
|
|
|||
|
|
@ -70,5 +70,8 @@ python-slugify==4.0.0
|
|||
# Export to Excel
|
||||
XlsxWriter==1.2.6
|
||||
|
||||
# Export to Doc
|
||||
python-docx==0.8.10
|
||||
|
||||
# For recursive fields
|
||||
djangorestframework-recursive==0.1.2
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user