diff --git a/apps/main/models.py b/apps/main/models.py index 83109f40..d8860aee 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -481,6 +481,19 @@ class Panel(ProjectBaseMixin): columns = [col[0] for col in cursor.description] return columns + def get_headers(self): + with connections['default'].cursor() as cursor: + try: + cursor.execute(self.query) + except Exception as er: + raise UnprocessableEntityError() + return self._raw_columns(cursor) + + def get_data(self): + with connections['default'].cursor() as cursor: + cursor.execute(self.query) + return cursor.fetchall() + def _raw_page(self, raw, request): page = request.query_params.get('page', 0) page_size = request.query_params.get('page_size', 0) diff --git a/apps/main/tasks.py b/apps/main/tasks.py new file mode 100644 index 00000000..0231b83f --- /dev/null +++ b/apps/main/tasks.py @@ -0,0 +1,14 @@ +"""Task methods for main app.""" + +from celery import shared_task + +from account.models import User +from main.models import Panel +from utils.export import SendExport + + +@shared_task +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() diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py index a2049a42..3d39f008 100644 --- a/apps/main/urls/back.py +++ b/apps/main/urls/back.py @@ -24,8 +24,9 @@ urlpatterns = [ name='page-types-list-create'), path('panels/', views.PanelsListCreateView.as_view(), name='panels'), path('panels//', views.PanelsRUDView.as_view(), name='panels-rud'), - path('panels//execute/', views.PanelsExecuteView.as_view(), name='panels-execute') - + path('panels//execute/', views.PanelsExecuteView.as_view(), name='panels-execute'), + path('panels//csv/', views.PanelsExportCSVView.as_view(), name='panels-csv'), + path('panels//xls/', views.PanelsExecuteXLSView.as_view(), name='panels-xls') ] diff --git a/apps/main/views/back.py b/apps/main/views/back.py index 98398f17..e819b71d 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -1,10 +1,12 @@ from django.contrib.contenttypes.models import ContentType +from django.utils.translation import gettext_lazy as _ from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions +from rest_framework import generics, permissions, status from rest_framework.generics import get_object_or_404 from rest_framework.response import Response from main import serializers +from main import tasks from main.filters import AwardFilter from main.models import Award, Footer, PageType, Panel from main.views import SiteSettingsView, SiteListView @@ -121,3 +123,35 @@ class PanelsExecuteView(generics.ListAPIView): def list(self, request, *args, **kwargs): panel = get_object_or_404(Panel, id=self.kwargs['pk']) return Response(panel.execute_query(request)) + + +class PanelsExportCSVView(PanelsExecuteView): + """Export panels via csv view.""" + permission_classes = (permissions.IsAdminUser,) + queryset = Panel.objects.all() + + def list(self, request, *args, **kwargs): + panel = get_object_or_404(Panel, id=self.kwargs['pk']) + # make task for celery + tasks.send_export_to_email.delay( + panel_id=panel.id, user_id=request.user.id) + return Response( + {"success": _('The file will be sent to your email.')}, + status=status.HTTP_200_OK + ) + + +class PanelsExecuteXLSView(PanelsExecuteView): + """Export panels via xlsx view.""" + permission_classes = (permissions.IsAdminUser,) + queryset = Panel.objects.all() + + def list(self, request, *args, **kwargs): + panel = get_object_or_404(Panel, id=self.kwargs['pk']) + # make task for celery + tasks.send_export_to_email.delay( + panel_id=panel.id, user_id=request.user.id, file_type='xls') + return Response( + {"success": _('The file will be sent to your email.')}, + status=status.HTTP_200_OK + ) diff --git a/apps/utils/export.py b/apps/utils/export.py new file mode 100644 index 00000000..e4756b09 --- /dev/null +++ b/apps/utils/export.py @@ -0,0 +1,115 @@ +import csv +import xlsxwriter +import logging +import os +import tempfile +from smtplib import SMTPException + +from django.conf import settings +from django.core.mail import EmailMultiAlternatives + +logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO) +logger = logging.getLogger(__name__) + + +class SendExport: + + 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 + 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() + + 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 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) + for row in self.get_data(): + file_writer.writerow(row) + + def make_xls_file(self): + headings = self.get_headers() + if not self.success: + return + with xlsxwriter.Workbook(self.file_path) as workbook: + worksheet = workbook.add_worksheet() + + # Add a bold format to use to highlight cells. + bold = workbook.add_format({'bold': True}) + + # Add the worksheet data that the charts will refer to. + data = self.get_data() + + worksheet.write_row('A1', headings, bold) + for n, row in enumerate(data): + 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 send_email(self): + + msg = EmailMultiAlternatives( + subject=self.email_subject, + body=self.email_body, + from_email=self.email_from, + to=[ + self.user.email, + 'kuzmenko.da@gmail.com', + 'sinapsit@yandex.ru' + ] + ) + + # Create an inline attachment + if self.file_path and self.success: + msg.attach_file(self.file_path) + else: + msg.body = 'An error occurred while executing the request.' + + try: + 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 diff --git a/requirements/base.txt b/requirements/base.txt index 90e5b2d5..18bb1bc5 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -63,3 +63,6 @@ pycountry==19.8.18 # sql-tree django-mptt==0.9.1 + +# Export to Excel +XlsxWriter==1.2.6 \ No newline at end of file