see changes

This commit is contained in:
Anatoly 2019-12-27 14:46:36 +03:00
parent 02d0112d8b
commit b41c185a65
10 changed files with 406 additions and 91 deletions

View File

@ -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,

View File

@ -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',
]
]

View File

@ -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

View File

@ -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)

View File

@ -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."""

View File

@ -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."""

View File

@ -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):

View File

@ -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())

View File

@ -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])])}"

View File

@ -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