From 43b6c1c999d9102ef6fd606371bbc1d560713f4a Mon Sep 17 00:00:00 2001 From: Raphael Odini Date: Tue, 28 Jan 2025 13:23:17 +0100 Subject: [PATCH] =?UTF-8?q?remplace=20la=20classe=20CSVImportApiView=20par?= =?UTF-8?q?=20un=20fichier=20utils=20d=C3=A9di=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/diagnostic.py | 10 ++++++---- api/views/diagnosticimport.py | 8 ++++---- api/views/purchaseimport.py | 11 ++++++----- api/views/utils.py | 24 ------------------------ common/utils/file_import.py | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 common/utils/file_import.py diff --git a/api/views/diagnostic.py b/api/views/diagnostic.py index f4029f4cfc..778c009678 100644 --- a/api/views/diagnostic.py +++ b/api/views/diagnostic.py @@ -13,6 +13,7 @@ from rest_framework.exceptions import NotFound, PermissionDenied from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView from rest_framework.pagination import LimitOffsetPagination +from rest_framework.views import APIView from api.exceptions import DuplicateException from api.permissions import ( @@ -22,7 +23,8 @@ IsCanteenManager, ) from api.serializers import DiagnosticAndCanteenSerializer, ManagerDiagnosticSerializer -from api.views.utils import CSVImportApiView, update_change_reason_with_auth +from api.views.utils import update_change_reason_with_auth +from common import file_import as file_import from common.utils import send_mail from data.models import Canteen, Teledeclaration from data.models.diagnostic import Diagnostic @@ -92,14 +94,14 @@ def perform_update(self, serializer): update_change_reason_with_auth(self, diagnostic) -class EmailDiagnosticImportFileView(CSVImportApiView): +class EmailDiagnosticImportFileView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: self.file = request.data["file"] - super()._verify_file_size() - super()._verify_file_format() + file_import.validate_file_size(self.file) + file_import.validate_file_format(self.file) email = request.data.get("email", request.user.email).strip() context = { "from": email, diff --git a/api/views/diagnosticimport.py b/api/views/diagnosticimport.py index 5891596f98..cf221827dd 100644 --- a/api/views/diagnosticimport.py +++ b/api/views/diagnosticimport.py @@ -19,7 +19,7 @@ from api.permissions import IsAuthenticated from api.serializers import FullCanteenSerializer -from api.views.utils import CSVImportApiView +from common import file_import as file_import from data.models import Canteen, ImportFailure, ImportType, Sector from data.models.diagnostic import Diagnostic from data.models.teledeclaration import Teledeclaration @@ -30,7 +30,7 @@ logger = logging.getLogger(__name__) -class ImportDiagnosticsView(ABC, CSVImportApiView): +class ImportDiagnosticsView(ABC): permission_classes = [IsAuthenticated] value_error_regex = re.compile(r"Field '(.+)' expected .+? got '(.+)'.") annotated_sectors = Sector.objects.annotate(name_lower=Lower("name")) @@ -79,8 +79,8 @@ def post(self, request): try: with transaction.atomic(): self.file = request.data["file"] - super()._verify_file_size() - super()._verify_file_format() + file_import.validate_file_size(self.file) + file_import.validate_file_format(self.file) self._process_file(self.file) if self.errors: diff --git a/api/views/purchaseimport.py b/api/views/purchaseimport.py index df34988df4..8d5f0ca267 100644 --- a/api/views/purchaseimport.py +++ b/api/views/purchaseimport.py @@ -12,10 +12,11 @@ from django.http import JsonResponse from rest_framework import status from rest_framework.exceptions import PermissionDenied +from rest_framework.views import APIView from api.permissions import IsAuthenticated from api.serializers import PurchaseSerializer -from api.views.utils import CSVImportApiView +from common import file_import as file_import from data.models import Canteen, ImportFailure, ImportType, Purchase from .utils import camelize, decode_bytes, normalise_siret @@ -23,7 +24,7 @@ logger = logging.getLogger(__name__) -class ImportPurchasesView(CSVImportApiView): +class ImportPurchasesView(APIView): permission_classes = [IsAuthenticated] max_error_items = 30 @@ -48,10 +49,10 @@ def post(self, request): logger.info("Purchase bulk import started") try: self.file = request.data["file"] - super()._verify_file_size() - super()._verify_file_format() + file_import.validate_file_size(self.file) + file_import.validate_file_format(self.file) - self.file_digest = super()._get_file_digest() + self.file_digest = file_import.get_file_digest(self.file) self._check_duplication() with transaction.atomic(): diff --git a/api/views/utils.py b/api/views/utils.py index a184aa03aa..e72dc355c8 100644 --- a/api/views/utils.py +++ b/api/views/utils.py @@ -1,37 +1,13 @@ -import hashlib import json import logging import chardet -from django.conf import settings -from django.core.exceptions import ValidationError from djangorestframework_camel_case.render import CamelCaseJSONRenderer -from rest_framework.views import APIView from simple_history.utils import update_change_reason logger = logging.getLogger(__name__) -class CSVImportApiView(APIView): - def _verify_file_size(self): - if self.file.size > settings.CSV_IMPORT_MAX_SIZE: - raise ValidationError( - f"Ce fichier est trop grand, merci d'utiliser un fichier de moins de {settings.CSV_IMPORT_MAX_SIZE_PRETTY}" - ) - - def _verify_file_format(self): - if self.file.content_type not in ["text/csv", "text/tab-separated-values"]: - raise ValidationError( - f"Ce fichier est au format {self.file.content_type}, merci d'exporter votre fichier au format CSV et réessayer." - ) - - def _get_file_digest(self): - file_hash = hashlib.md5() - for row in self.file: - file_hash.update(row) - return file_hash.hexdigest() - - def camelize(data): camel_case_bytes = CamelCaseJSONRenderer().render(data) return json.loads(camel_case_bytes.decode("utf-8")) diff --git a/common/utils/file_import.py b/common/utils/file_import.py new file mode 100644 index 0000000000..b84c107c8e --- /dev/null +++ b/common/utils/file_import.py @@ -0,0 +1,34 @@ +import hashlib + +from django.conf import settings +from django.core.exceptions import ValidationError + + +def get_file_size(file): + return file.size + + +def validate_file_size(file): + if get_file_size(file) > settings.CSV_IMPORT_MAX_SIZE: + raise ValidationError( + f"Ce fichier est trop grand, merci d'utiliser un fichier de moins de {settings.CSV_IMPORT_MAX_SIZE_PRETTY}" + ) + + +def get_file_content_type(file): + return file.content_type + + +def validate_file_format(file): + file_format = get_file_content_type(file) + if file_format not in ["text/csv", "text/tab-separated-values"]: + raise ValidationError( + f"Ce fichier est au format {file_format}, merci d'exporter votre fichier au format CSV et réessayer." + ) + + +def get_file_digest(file): + file_hash = hashlib.md5() + for row in file: + file_hash.update(row) + return file_hash.hexdigest()