diff --git a/Database/Функции/application_patch.sql b/Database/Функции/application_patch.sql index a31406d..1559928 100644 --- a/Database/Функции/application_patch.sql +++ b/Database/Функции/application_patch.sql @@ -5,7 +5,7 @@ RETURNS TABLE(confirm boolean) AS $$ BEGIN UPDATE nn_db.application - SET nn_db.application.fk_status = in_fk_status + SET fk_status = in_fk_status WHERE nn_db.application.pk_application = in_pk_application; diff --git a/Project/nn_project/api/views.py b/Project/nn_project/api/views.py index 2770b2b..a0dae21 100644 --- a/Project/nn_project/api/views.py +++ b/Project/nn_project/api/views.py @@ -7,9 +7,10 @@ from django.core.signing import TimestampSigner from django.utils.encoding import force_str, force_bytes from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.contrib.auth.hashers import PBKDF2PasswordHasher +from django.http.request import QueryDict from django.views.decorators.csrf import csrf_exempt -import re +import re, json def call_func(name: str, parameters: dict): @@ -31,41 +32,57 @@ def call_func(name: str, parameters: dict): return [dict(zip(columns, row)) for row in cursor.fetchall()] def api_get_xcsrf(request): - data = { 'X-CSFRToken': get_token(request) } + data = { 'X-CSRFToken': get_token(request) } return JsonResponse(data, safe=False, status=200) +# Проверка авторизации def is_auth(request): + # Проверяем наличие заголовка авторизации if not request.headers.get('Authorization'): + # Если его нет - возвращаем "Ложь" return False + # Если токен есть - получаем его, разбивая строку по пробелу, при этом извлекая только последнюю строку token = request.headers.get('Authorization').split(' ')[-1] + # Инициализируем подписывающий объект signer = TimestampSigner(salt='django.core.signing') try: + # Пробуем извлечь из токена имя пользователя login = signer.unsign(force_str(urlsafe_base64_decode(token)), max_age=1000) - print(login) except: + # Если не получилось - возвращаем "Ложь" return False else: + # Если получилось - запрашиваем в базе данных информацию о пользователе user = call_func('nn_db.user_get', {'search_value': login})[0] + # И возвращаем полученные данные return user +# Обработка заявок def api_applications(request): + # Проверяем, авторизован ли пользователь user = is_auth(request) if not user: + # Если пользователь не авторизован - возвращаем ответ с соответствующим сообщением return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403) + + # Если пользователь авторизован, то проверяем, какой тип запроса был отправлен if request.method == 'GET': - params = {} - if user['is_admin']: - params['pk_user'] = None + # Если запрос на получение данных + params = {} # Инициализируем объект, хранящий параметры запроса + if user['is_admin']: # Если пользователь - администратор + params['pk_user'] = None # Заполняем параметр null-значением (в Python - None) else: - params['pk_user'] = str(user['pk_user']) - # params['pk_user'] = None + params['pk_user'] = str(user['pk_user']) # Иначе заполняем параметр идентификатором пользователя + + # Запрашиваем данные по заявкам из базы данных по указанному пользователю results = call_func('nn_db.application_get', params) + # Определяем списки для возвращаемых заявок и ключей, заявки по которым уже есть в первом списке applications = [] keys = [] - for result in results: - if result['pk_application'] not in keys: - application = {} + for result in results: # Проходимся по каждой строке результата из БД + if result['pk_application'] not in keys: # Если ключ текущей заявки не находится в списке ключей + application = {} # Инициализируем словарь и заполняем его данными из ответа БД application['pk_application'] = result['pk_application'] application['car_number'] = result['car_number'] application['description'] = result['description'] @@ -73,33 +90,140 @@ def api_applications(request): application['title'] = result['title'] application['fk_user'] = result['fk_user'] application['username'] = result['username'] + # Инициализируем список хранения данных об изображениях к заявке application['images'] = [] - + + # Если есть данные о изображении if result['pk_image'] is not None: - image = {} + image = {} # Инициализируем словарь картинки и заполняем его данными из ответа БД image['pk_image'] = result['pk_image'] image['file_name'] = result['file_name'] image['file_ext'] = result['file_ext'] + # Добавляем словарь картинки в список картинок заявки application['images'].append(image) + # Добавляем словарь заявки в список заявок applications.append(application) + # Добавляем ключ заявки в список обработанных ключей keys.append(application['pk_application']) else: - for application in applications: - if application['pk_application'] == result['pk_application']: - image = {} + # Если заявка уже обработана, т.е. эта строка обозначает следующее изображение + for application in applications: # Проходимся по каждой заявке и ищем текущую заявку + if application['pk_application'] == result['pk_application']: # Если ключи совпадают + image = {} # Инициализируем словарь картинки и заполняем его данными из ответа БД image['pk_image'] = result['pk_image'] image['file_name'] = result['file_name'] image['file_ext'] = result['file_ext'] + # Добавляем словарь картинки в список картинок заявки application['images'].append(image) + + # Возвращаем данные пользователю return JsonResponse({"code": 200, "data": applications}, safe=False, status=200) + + # Если запрос на добавление данных if request.method == 'POST': - params = {} + params = {} # Инициализируем список параметров для запроса и заполняем его данными из POST-запроса params['car_number'] = request.POST.get('car_number') params['description'] = request.POST.get('description') - params['fk_user'] = user['pk_user'] + params['fk_user'] = str(user['pk_user']) + + # Инициализируем список ошибок валидации данных + errors = [] + # Автоматическая проверка параметров на заполненность + for key in params: + if params[key] is None or params[key] == '': + errors.append({ key: "Параметр обязателен для заполнения" }) + + # Если ошибки есть - то возвращаем ответ с ошибкой + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + # Проверяем введенные данные на валидность + if re.match(r'^[YETOPAHKXCBMУЕТОРАНКХСВМ]\d{3}[YETOPAHKXCBMУЕТОРАНКХСВМ]{2}\d{1,3}$', params['car_number']) is None: + errors.append({ "car_number": "Государственный номер автомобиля имеет некорректный формат" }) + if len(params['description']) < 15: + errors.append({ "description": "Описание нарушения должно быть длинее 5 символов" }) + + # Если ошибки есть - то возвращаем ответ с ошибкой + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + # Отправляем запрос на добавление данных confirm = call_func('nn_db.application_post', params) - return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201) + + # Проверяем, пришел ли в ответ на запрос первичный ключ добавленной заявки + if any(confirm) and 'pk_application' in confirm[0]: + # Если пришёл - то возвращаем данные + return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201) + else: + # Если нет - то возвращаем ошибку с сообщением + return JsonResponse({"code": 520, "message": "Не удалось добавить запись в БД, попробуйте позднее"}, safe=False, status=520) + + # Если запрос на изменение заявки: + if request.method == 'PATCH': + if not user['is_admin']: # Если пользователь - не администратор + return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403) # По ТЗ смена статуса - только у администратора + + # Запросы PUT, PATCH и DELETE не работают с Form-data нативно, по этому в теле запроса должен быть JSON объект + data = json.loads(request.body) + + params = {} # Инициализируем список параметров для запроса и заполняем его данными из JSON объекта + params['pk_application'] = str(data.get('pk_application')) + params['fk_status'] = str(data.get('fk_status')) + + # Инициализируем список ошибок валидации данных + errors = [] + # Автоматическая проверка параметров на заполненность + for key in params: + if params[key] is None or params[key] == '': + errors.append({ key: "Параметр обязателен для заполнения" }) + + # Если ошибки есть - то возвращаем ответ с ошибкой + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + confirm = call_func('nn_db.application_patch', params) + + # Если ответ не пустой и есть поле подтверждения + if any(confirm) and 'confirm' in confirm[0]: + if confirm[0]['confirm']: + # Если запись обновлена - возвращаем 202 ответ с сообщением + return JsonResponse({"code": 202, "message": "Данные обновлены"}, safe=False, status=202) + else: + # Если запись не обновлена - то возвращаем ответ 304 с сообщением + return JsonResponse({"code": 304, "message": "Данные не обновлены"}, safe=False, status=304) + else: + # Если нет - то возвращаем ошибку с сообщением + return JsonResponse({"code": 520, "message": "Не удалось обновить запись в БД, попробуйте позднее"}, safe=False, status=520) + +def api_statuses(request): + user = is_auth(request) + if not user: + return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403) + + if request.method == 'GET': + results = call_func('nn_db.status_get', {}) + return JsonResponse({"code": 200, "data": results}, safe=False, status=200) + + if request.method == 'POST': + params = {} + params['title'] = request.POST.get('title') + + if params['title'] is None: + return JsonResponse({"code": 409, "errors": [{"title": "Параметр обязателен для заполнения"}]}, safe=False, status=409) + + if len(params['title']) <= 4: + return JsonResponse({"code": 409, "errors": [{"title": "Описание статуса должно быть длинее 4 символов"}]}, safe=False, status=409) + + confirm = call_func('nn_db.status_post', params) + + # Проверяем, пришел ли в ответ на запрос первичный ключ добавленной заявки + if any(confirm) and 'pk_status' in confirm[0]: + # Если пришёл - то возвращаем данные + return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201) + else: + # Если нет - то возвращаем ошибку с сообщением + return JsonResponse({"code": 520, "message": "Не удалось добавить запись в БД, попробуйте позднее"}, safe=False, status=520) def api_users(request): if is_auth(request): diff --git a/Project/nn_project/nn_project/urls.py b/Project/nn_project/nn_project/urls.py index b1a9cf3..ebe6658 100644 --- a/Project/nn_project/nn_project/urls.py +++ b/Project/nn_project/nn_project/urls.py @@ -20,6 +20,7 @@ from api import views urlpatterns = [ path('api/applications/', views.api_applications) + , path('api/statuses/', views.api_statuses) , path('api/get_xcsrf/', views.api_get_xcsrf) , path('api/users/', views.api_users) , path('api/auth/', views.api_auth)