diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index 2b137571..32d97a3d 100644 --- a/apps/collection/serializers/common.py +++ b/apps/collection/serializers/common.py @@ -230,3 +230,32 @@ class AdvertorialBaseSerializer(serializers.ModelSerializer): attrs['guide_element'] = guide_element return attrs + + +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) + toque_number = serializers.CharField(source='establishment.toque_number', default=None) + + class Meta: + model = models.GuideElement + fields = [ + 'id', + 'node_name', + # 'establishment', + # 'review', + # 'wine_region', + # 'product_detail', + # 'priority', + 'city_name', + # 'section_name', + # 'wine_color_section_name', + # 'children', + # 'label_photo', + 'name', + 'public_mark', + 'toque_number' + + ] \ No newline at end of file diff --git a/apps/collection/tasks.py b/apps/collection/tasks.py index d97071ab..6eb9fcdd 100644 --- a/apps/collection/tasks.py +++ b/apps/collection/tasks.py @@ -127,3 +127,22 @@ def generate_product_guide_elements(guide_id: int, queryset_values: dict): f'DETAIL: Guide ID {guide_id} - {e}') else: guide.update_count_related_objects() + + +@shared_task +def export_guide(guide_id, user_id, file_type='csv'): + from collection.models import GuideElement, Guide + from collection.serializers import GuideElementExportSerializer + from account.models import User + from utils.export import SendGuideExport + + user = User.objects.get(id=user_id) + guide = Guide.objects.get(id=guide_id) + root = GuideElement.objects.get_root_node(guide) + if root: + nodes = root.get_descendants() + serializer = GuideElementExportSerializer(nodes, many=True) + data = serializer.data + SendGuideExport( + data=data, guide=guide, user=user, file_type=file_type + ).send() diff --git a/apps/collection/urls/back.py b/apps/collection/urls/back.py index 1de62687..7431ae2c 100644 --- a/apps/collection/urls/back.py +++ b/apps/collection/urls/back.py @@ -24,4 +24,10 @@ urlpatterns = [ name='guide-advertorial-destroy'), path('guides//filters/', views.GuideFilterCreateView.as_view(), name='guide-filter-list-create'), + path('guides//export-csv/', views.GuideElementExportCSVView.as_view(), + name='guide-export-csv'), + path('guides//export-xml/', views.GuideElementExportXMLView.as_view(), + name='guide-export-xml'), + path('guides//export-doc/', views.GuideElementExportDOCView.as_view(), + name='guide-export-doc'), ] + router.urls diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index 856f5dbd..2cce236d 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -1,12 +1,14 @@ +from django.shortcuts import get_object_or_404 +from django.utils.translation import gettext_lazy as _ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics from rest_framework import mixins, permissions, viewsets from rest_framework import status from rest_framework.filters import OrderingFilter from rest_framework.response import Response -from django.shortcuts import get_object_or_404 from collection import models, serializers +from collection import tasks from utils.views import BindObjectMixin @@ -161,3 +163,35 @@ class AdvertorialDestroyView(AdvertorialBaseView, def delete(self, request, *args, **kwargs): """Overridden delete method.""" return self.destroy(request, *args, **kwargs) + + +class GuideElementExportCSVView(generics.ListAPIView): + """Guide element export csv view.""" + pagination_class = None + 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) + return Response({"success": _('The file will be sent to your email.')}, + status=status.HTTP_200_OK) + + +class GuideElementExportXMLView(generics.ListAPIView): + """Guide element export xml view.""" + pagination_class = None + queryset = models.GuideElement.objects.all() + serializer_class = serializers.GuideElementBaseSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + +class GuideElementExportDOCView(generics.ListAPIView): + """Guide element export doc view.""" + pagination_class = None + queryset = models.GuideElement.objects.all() + serializer_class = serializers.GuideElementBaseSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index bdfe4bb3..e751e4d4 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -646,6 +646,7 @@ class EstablishmentGuideElementSerializer(serializers.ModelSerializer): fields = [ 'id', 'type', + 'name', 'subtypes', 'address', 'tz', diff --git a/apps/main/tasks.py b/apps/main/tasks.py index 0231b83f..3ea8a4f0 100644 --- a/apps/main/tasks.py +++ b/apps/main/tasks.py @@ -11,4 +11,4 @@ from utils.export import SendExport def send_export_to_email(panel_id, user_id, file_type='csv'): panel = Panel.objects.get(id=panel_id) user = User.objects.get(id=user_id) - SendExport(user, panel, file_type).send() + SendExport(user, panel=panel, file_type=file_type).send() diff --git a/apps/utils/export.py b/apps/utils/export.py index e4756b09..71fa956a 100644 --- a/apps/utils/export.py +++ b/apps/utils/export.py @@ -7,46 +7,48 @@ from smtplib import SMTPException from django.conf import settings from django.core.mail import EmailMultiAlternatives +import abc logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) -class SendExport: +class SendExportBase: + """Base class of export and sending data.""" - def __init__(self, user, panel, file_type='csv'): - self.type_mapper = { - "csv": self.make_csv_file, - "xls": self.make_xls_file - } - self.file_type = file_type - self.user = user - self.panel = panel + def __init__(self): + self.success = False self.email_from = settings.EMAIL_HOST_USER self.email_subject = f'Export panel: {self.get_file_name()}' self.email_body = 'Exported panel data' - self.get_file_method = self.type_mapper[file_type] self.file_path = os.path.join( settings.STATIC_ROOT, 'email', tempfile.gettempdir(), self.get_file_name() ) - self.success = False - - def get_file_name(self): - name = '_'.join(self.panel.name.split(' ')) - return f'export_{name.lower()}.{self.file_type}' - - def get_data(self): - return self.panel.get_data() + self.type_mapper = { + "csv": self.make_csv_file, + "xls": self.make_xls_file + } + @abc.abstractmethod def get_headers(self): - try: - header = self.panel.get_headers() - self.success = True - return header - except Exception as err: - logger.info(f'HEADER:{err}') + pass + + @abc.abstractmethod + def get_emails_to(self): + return [ + 'kuzmenko.da@gmail.com', + 'sinapsit@yandex.ru' + ] + + @abc.abstractmethod + def get_data(self): + pass + + @abc.abstractmethod + def get_file_name(self): + return '' def make_csv_file(self): file_header = self.get_headers() @@ -77,17 +79,8 @@ class SendExport: worksheet.write_row(f'A{n+2}', [str(i) for i in row]) workbook.close() - def send(self): - self.get_file_method() - print(f'ok: {self.file_path}') - self.send_email() - - def get_file(self): - if os.path.exists(self.file_path) and os.path.isfile(self.file_path): - with open(self.file_path, 'rb') as export_file: - return export_file - else: - logger.info('COMMUTATOR:image file not found dir: {path}') + def make_xml_file(self): + pass def send_email(self): @@ -95,11 +88,7 @@ class SendExport: subject=self.email_subject, body=self.email_body, from_email=self.email_from, - to=[ - self.user.email, - 'kuzmenko.da@gmail.com', - 'sinapsit@yandex.ru' - ] + to=self.get_emails_to() ) # Create an inline attachment @@ -112,4 +101,107 @@ class SendExport: msg.send() logger.debug(f"COMMUTATOR:Email successfully sent") except SMTPException as e: - logger.error(f"COMMUTATOR:Email connector: {e}") \ No newline at end of file + logger.error(f"COMMUTATOR:Email connector: {e}") + + @abc.abstractmethod + def send(self): + pass + + +class SendExport(SendExportBase): + + def __init__(self, panel, user, file_type='csv', **kwargs): + super().__init__() + self.panel = panel + self.user = user + self.file_type = file_type + self.get_file_method = self.type_mapper[file_type] + + def get_emails_to(self): + return [self.user.email] + super().get_emails_to() + + def get_file_name(self): + name = '_'.join(self.panel.name.split(' ')) + return f'export_{name.lower()}.{self.file_type}' + + def get_data(self): + return self.panel.get_data() + + def get_headers(self): + try: + header = self.panel.get_headers() + self.success = True + return header + except Exception as err: + logger.info(f'HEADER:{err}') + + def get_file(self): + if os.path.exists(self.file_path) and os.path.isfile(self.file_path): + with open(self.file_path, 'rb') as export_file: + return export_file + else: + logger.info('COMMUTATOR:image file not found dir: {path}') + + def send(self): + self.get_file_method() + print(f'ok: {self.file_path}') + self.send_email() + + +class SendGuideExport(SendExportBase): + """Send guid export.""" + + def __init__(self, data, guide, user, file_type='csv', **kwargs): + + self.type_mapper = { + "csv": self.make_csv_file, + "xml": self.make_xml_file, + "doc": self.make_doc_file + } + self.guide = guide + self.data = data + self.user = user + self.file_type = file_type + self.get_file_method = self.type_mapper[file_type] + super().__init__() + + def get_file_name(self): + name = self.guide.slug + return f'export_{name}.{self.file_type}' + + def make_doc_file(self): + pass + + 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): + return self.data + + def send(self): + self.get_file_method() + print(f'ok: {self.file_path}') + self.send_email() + + def make_csv_file(self): + file_header = self.get_headers() + if not self.success: + return + + with open(self.file_path, 'w') as f: + file_writer = csv.writer(f, quotechar='"', quoting=csv.QUOTE_MINIMAL) + # Write headers to CSV file + file_writer.writerow(file_header) + 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": + file_writer.writerow(row.values()) \ No newline at end of file diff --git a/project/settings/local.py b/project/settings/local.py index 5581a50d..311da9f9 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -112,3 +112,17 @@ TESTING = sys.argv[1:2] == ['test'] if TESTING: ELASTICSEARCH_INDEX_NAMES = {} ELASTICSEARCH_DSL_AUTOSYNC = False + +# Email settings +SERVER_EMAIL = 'honyl@yandex.ru' +EMAIL_HOST = 'smtp.yandex.ru' +EMAIL_HOST_USER = 'honyl@yandex.ru' +EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') +EMAIL_PORT = '587' +EMAIL_USE_TLS = True +EMAIL_SUBJECT = 'Technical email from LunchBox' +EMAIL_FROM = 'honyl@yandex.ru' +EMAIL_TO = [ + 'd.kuzmenko@spider.ru', + # 'g.baranova@spider.ru' +] \ No newline at end of file