All checks were successful
CI/CD Pipeline / Manual Action Help (push) Has been skipped
CI/CD Pipeline / Start Dev Containers in Dokploy (push) Has been skipped
CI/CD Pipeline / Drop and Recreate Dev Database (push) Has been skipped
CI/CD Pipeline / Quality Gate (push) Successful in 53s
CI/CD Pipeline / Build and Push Images (push) Successful in 3m28s
CI/CD Pipeline / Internal Notify (push) Successful in 1s
CI/CD Pipeline / Deploy Dev in Dokploy (push) Successful in 1s
165 lines
5.7 KiB
Python
165 lines
5.7 KiB
Python
"""Frontend-oriented parser result API v1 routes."""
|
||
|
||
from apps.parsers.serializers import (
|
||
ParserResultRecordSerializer,
|
||
ParserRunResponseSerializer,
|
||
)
|
||
from apps.parsers.source_registry import PARSER_SOURCES, ParserSourceDescriptor
|
||
from apps.parsers.views import (
|
||
RESULT_DETAIL_PARAMS,
|
||
RESULT_LIST_PARAMS,
|
||
UPLOAD_FILE_PARAM,
|
||
ParserUploadView,
|
||
SourceResultDetailView,
|
||
SourceResultListView,
|
||
source_result_swagger_tag,
|
||
)
|
||
from django.urls import path
|
||
from drf_yasg.utils import swagger_auto_schema
|
||
|
||
app_name = "parser_results"
|
||
ROUTE_TITLES = {
|
||
"eis/contracts": "ЕИС Контракты",
|
||
"eis/procurements-44fz": "ЕИС Закупки 44-ФЗ",
|
||
"eis/procurements-223fz": "ЕИС Закупки 223-ФЗ",
|
||
"zakupki": "ЕИС Закупки",
|
||
"proverki": "Проверки Генпрокуратуры",
|
||
}
|
||
|
||
|
||
def _result_source_descriptors():
|
||
"""Вернуть уникальные источники с явным API route."""
|
||
seen_routes = set()
|
||
for descriptor in PARSER_SOURCES.values():
|
||
if not descriptor.api_route or descriptor.api_route in seen_routes:
|
||
continue
|
||
seen_routes.add(descriptor.api_route)
|
||
yield descriptor
|
||
|
||
|
||
def _upload_source_descriptors():
|
||
"""Вернуть уникальные источники с route ручной загрузки."""
|
||
seen_routes = set()
|
||
for descriptor in PARSER_SOURCES.values():
|
||
if not descriptor.supports_file_upload:
|
||
continue
|
||
upload_route = descriptor.upload_api_route
|
||
if not upload_route or upload_route in seen_routes:
|
||
continue
|
||
seen_routes.add(upload_route)
|
||
yield descriptor
|
||
|
||
|
||
def _route_title(descriptor: ParserSourceDescriptor) -> str:
|
||
"""Вернуть человекочитаемое название публичного route."""
|
||
return ROUTE_TITLES.get(descriptor.api_route, descriptor.title)
|
||
|
||
|
||
def _route_description(descriptor: ParserSourceDescriptor) -> str:
|
||
"""Вернуть описание данных, опубликованных на одном route."""
|
||
scopes = [
|
||
item.data_scope
|
||
for item in PARSER_SOURCES.values()
|
||
if item.api_route == descriptor.api_route
|
||
]
|
||
return "; ".join(dict.fromkeys(scopes))
|
||
|
||
|
||
def _list_view(descriptor: ParserSourceDescriptor):
|
||
tag = source_result_swagger_tag(descriptor.key)
|
||
title = _route_title(descriptor)
|
||
description = _route_description(descriptor)
|
||
|
||
class SourceListView(SourceResultListView):
|
||
source_key = descriptor.key
|
||
|
||
@swagger_auto_schema(
|
||
operation_summary=f"{title}: get_list",
|
||
operation_description=(
|
||
f"Список записей источника: {description}. "
|
||
"Фильтры передаются query-параметрами."
|
||
),
|
||
manual_parameters=RESULT_LIST_PARAMS,
|
||
tags=[tag],
|
||
responses={200: ParserResultRecordSerializer(many=True)},
|
||
)
|
||
def get(self, request):
|
||
return super().get(request)
|
||
|
||
return SourceListView.as_view()
|
||
|
||
|
||
def _detail_view(descriptor: ParserSourceDescriptor):
|
||
tag = source_result_swagger_tag(descriptor.key)
|
||
title = _route_title(descriptor)
|
||
description = _route_description(descriptor)
|
||
|
||
class SourceDetailView(SourceResultDetailView):
|
||
source_key = descriptor.key
|
||
|
||
@swagger_auto_schema(
|
||
operation_summary=f"{title}: get",
|
||
operation_description=(
|
||
f"Одна запись источника: {description}. "
|
||
"Query-параметры дополнительно сужают выборку."
|
||
),
|
||
manual_parameters=RESULT_DETAIL_PARAMS,
|
||
tags=[tag],
|
||
responses={200: ParserResultRecordSerializer, 404: "Запись не найдена"},
|
||
)
|
||
def get(self, request, pk: int):
|
||
return super().get(request, pk=pk)
|
||
|
||
return SourceDetailView.as_view()
|
||
|
||
|
||
def _upload_view(descriptor: ParserSourceDescriptor):
|
||
tag = source_result_swagger_tag(descriptor.key)
|
||
|
||
class SourceUploadView(ParserUploadView):
|
||
@swagger_auto_schema(
|
||
operation_summary=f"{descriptor.title}: загрузить файл",
|
||
operation_description=(
|
||
f"Ручная загрузка файла для источника: {descriptor.data_scope}. "
|
||
"Файл обрабатывается через Celery."
|
||
),
|
||
manual_parameters=[UPLOAD_FILE_PARAM],
|
||
consumes=["multipart/form-data"],
|
||
tags=[tag],
|
||
responses={202: ParserRunResponseSerializer, 400: "Ошибка валидации"},
|
||
)
|
||
def post(self, request):
|
||
return super().post(request, source_key=descriptor.key)
|
||
|
||
return SourceUploadView.as_view()
|
||
|
||
|
||
urlpatterns = []
|
||
for source_descriptor in _result_source_descriptors():
|
||
route_name = source_descriptor.api_route.replace("/", "-")
|
||
urlpatterns.extend(
|
||
[
|
||
path(
|
||
f"{source_descriptor.api_route}/",
|
||
_list_view(source_descriptor),
|
||
name=f"{route_name}-get-list",
|
||
),
|
||
path(
|
||
f"{source_descriptor.api_route}/<int:pk>/",
|
||
_detail_view(source_descriptor),
|
||
name=f"{route_name}-get",
|
||
),
|
||
]
|
||
)
|
||
|
||
for source_descriptor in _upload_source_descriptors():
|
||
upload_route = source_descriptor.upload_api_route
|
||
route_name = upload_route.replace("/", "-")
|
||
urlpatterns.append(
|
||
path(
|
||
f"{upload_route}/",
|
||
_upload_view(source_descriptor),
|
||
name=f"{route_name}-upload",
|
||
)
|
||
)
|