Some checks failed
CI/CD Pipeline / Run Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Telegram Notify Success (push) Has been cancelled
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m54s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
457 lines
15 KiB
Python
457 lines
15 KiB
Python
"""
|
||
Сериализаторы для приложения парсеров.
|
||
|
||
Все сериализаторы read-only, так как данные загружаются только через парсеры.
|
||
"""
|
||
|
||
from apps.parsers.models import (
|
||
FinancialReport,
|
||
FinancialReportLine,
|
||
IndustrialCertificateRecord,
|
||
IndustrialProductRecord,
|
||
InspectionRecord,
|
||
ManufacturerRecord,
|
||
ParserLoadLog,
|
||
ProcurementRecord,
|
||
Proxy,
|
||
)
|
||
from rest_framework import serializers
|
||
|
||
# =============================================================================
|
||
# Минпромторг - Сертификаты промышленного производства
|
||
# =============================================================================
|
||
|
||
|
||
class IndustrialCertificateSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Сертификат промышленного производства РФ.
|
||
|
||
Данные загружаются из Минпромторга.
|
||
"""
|
||
|
||
class Meta:
|
||
model = IndustrialCertificateRecord
|
||
fields = [
|
||
"id",
|
||
"load_batch",
|
||
"issue_date",
|
||
"issue_date_normalized",
|
||
"certificate_number",
|
||
"expiry_date",
|
||
"expiry_date_normalized",
|
||
"certificate_file_url",
|
||
"organisation_name",
|
||
"inn",
|
||
"ogrn",
|
||
"registry_organization",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
# =============================================================================
|
||
# Минпромторг - Реестр производителей
|
||
# =============================================================================
|
||
|
||
|
||
class ManufacturerSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Производитель из реестра Минпромторга.
|
||
|
||
Данные загружаются из Минпромторга.
|
||
"""
|
||
|
||
class Meta:
|
||
model = ManufacturerRecord
|
||
fields = [
|
||
"id",
|
||
"load_batch",
|
||
"full_legal_name",
|
||
"inn",
|
||
"ogrn",
|
||
"address",
|
||
"registry_organization",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
class IndustrialProductSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Промышленная продукция из реестра Минпромторга.
|
||
|
||
Данные загружаются из Минпромторга.
|
||
"""
|
||
|
||
class Meta:
|
||
model = IndustrialProductRecord
|
||
fields = [
|
||
"id",
|
||
"load_batch",
|
||
"full_organisation_name",
|
||
"ogrn",
|
||
"inn",
|
||
"registry_number",
|
||
"product_name",
|
||
"product_model",
|
||
"okpd2_code",
|
||
"tnved_code",
|
||
"regulatory_document",
|
||
"registry_organization",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
# =============================================================================
|
||
# Единый реестр проверок (proverki.gov.ru)
|
||
# =============================================================================
|
||
|
||
|
||
class InspectionSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Проверка из Единого реестра проверок.
|
||
|
||
Поддерживает ФЗ-294 и ФЗ-248.
|
||
"""
|
||
|
||
class Meta:
|
||
model = InspectionRecord
|
||
fields = [
|
||
"id",
|
||
"load_batch",
|
||
"registration_number",
|
||
"inn",
|
||
"ogrn",
|
||
"organisation_name",
|
||
"control_authority",
|
||
"inspection_type",
|
||
"inspection_form",
|
||
"start_date",
|
||
"start_date_normalized",
|
||
"end_date",
|
||
"end_date_normalized",
|
||
"status",
|
||
"legal_basis",
|
||
"result",
|
||
"is_federal_law_248",
|
||
"data_year",
|
||
"data_month",
|
||
"registry_organization",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
# =============================================================================
|
||
# Государственные закупки (zakupki.gov.ru)
|
||
# =============================================================================
|
||
|
||
|
||
class ProcurementSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Государственная закупка из ЕИС zakupki.gov.ru.
|
||
|
||
Поддерживает 44-ФЗ и 223-ФЗ.
|
||
"""
|
||
|
||
class Meta:
|
||
model = ProcurementRecord
|
||
fields = [
|
||
"id",
|
||
"load_batch",
|
||
"purchase_number",
|
||
"purchase_name",
|
||
"customer_inn",
|
||
"customer_kpp",
|
||
"customer_ogrn",
|
||
"customer_name",
|
||
"max_price",
|
||
"max_price_amount",
|
||
"currency_code",
|
||
"placement_method",
|
||
"publish_date",
|
||
"publish_date_normalized",
|
||
"end_date",
|
||
"end_date_normalized",
|
||
"status",
|
||
"law_type",
|
||
"purchase_object_info",
|
||
"href",
|
||
"region_code",
|
||
"data_year",
|
||
"data_month",
|
||
"registry_organization",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
# =============================================================================
|
||
# ФНС - Бухгалтерская отчетность
|
||
# =============================================================================
|
||
|
||
|
||
class FinancialReportLineSerializer(serializers.ModelSerializer):
|
||
"""Строка финансового отчета."""
|
||
|
||
class Meta:
|
||
model = FinancialReportLine
|
||
fields = [
|
||
"id",
|
||
"form_code",
|
||
"line_code",
|
||
"line_name",
|
||
"year",
|
||
"period_start",
|
||
"period_end",
|
||
]
|
||
|
||
|
||
class FinancialReportSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Финансовый отчет ФНС.
|
||
|
||
Данные загружаются из Excel файлов.
|
||
"""
|
||
|
||
lines_count = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = FinancialReport
|
||
fields = [
|
||
"id",
|
||
"external_id",
|
||
"ogrn",
|
||
"registry_organization",
|
||
"file_name",
|
||
"file_hash",
|
||
"load_batch",
|
||
"status",
|
||
"source",
|
||
"error_message",
|
||
"created_at",
|
||
"updated_at",
|
||
"lines_count",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
def get_lines_count(self, obj) -> int:
|
||
return getattr(obj, "lines_count", obj.lines.count())
|
||
|
||
|
||
class FinancialReportDetailSerializer(FinancialReportSerializer):
|
||
"""Финансовый отчет с детализацией строк."""
|
||
|
||
lines = FinancialReportLineSerializer(many=True, read_only=True)
|
||
|
||
class Meta(FinancialReportSerializer.Meta):
|
||
fields = FinancialReportSerializer.Meta.fields + ["lines"]
|
||
|
||
|
||
class FNSFileUploadSerializer(serializers.Serializer):
|
||
"""
|
||
Сериализатор для загрузки файлов FNS.
|
||
|
||
Принимает список Excel файлов в формате fin_{id}_{ogrn}.xlsx
|
||
"""
|
||
|
||
files = serializers.ListField(
|
||
child=serializers.FileField(),
|
||
allow_empty=False,
|
||
help_text="Список файлов для загрузки (fin_*.xlsx)",
|
||
)
|
||
|
||
def validate_files(self, value):
|
||
"""Валидация файлов."""
|
||
import re
|
||
|
||
pattern = re.compile(r"^fin_\d+_\d{13,15}\.xlsx$")
|
||
|
||
for file in value:
|
||
if not pattern.match(file.name):
|
||
raise serializers.ValidationError(
|
||
f"Неверный формат имени файла: {file.name}. "
|
||
"Ожидается: fin_{{id}}_{{ogrn}}.xlsx"
|
||
)
|
||
|
||
return value
|
||
|
||
|
||
# =============================================================================
|
||
# Служебные модели
|
||
# =============================================================================
|
||
|
||
|
||
class ParserLoadLogSerializer(serializers.ModelSerializer):
|
||
"""
|
||
Лог загрузки парсера.
|
||
|
||
Информация о каждой загрузке данных из внешнего источника.
|
||
"""
|
||
|
||
source_display = serializers.CharField(source="get_source_display", read_only=True)
|
||
|
||
class Meta:
|
||
model = ParserLoadLog
|
||
fields = [
|
||
"id",
|
||
"batch_id",
|
||
"source",
|
||
"source_display",
|
||
"records_count",
|
||
"status",
|
||
"error_message",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
class ProxySerializer(serializers.ModelSerializer):
|
||
"""
|
||
Прокси-сервер для парсеров.
|
||
|
||
Используется для обхода блокировок при парсинге.
|
||
"""
|
||
|
||
class Meta:
|
||
model = Proxy
|
||
fields = [
|
||
"id",
|
||
"address",
|
||
"is_active",
|
||
"last_used_at",
|
||
"fail_count",
|
||
"description",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = fields
|
||
|
||
|
||
class SourceCardRefreshParamSerializer(serializers.Serializer):
|
||
"""Описание параметра ручного обновления карточки источника."""
|
||
|
||
name = serializers.CharField(read_only=True)
|
||
label = serializers.CharField(read_only=True)
|
||
description = serializers.CharField(read_only=True)
|
||
required = serializers.BooleanField(read_only=True)
|
||
type = serializers.CharField(read_only=True)
|
||
default = serializers.JSONField(read_only=True)
|
||
|
||
|
||
class SourceCardLoadSerializer(serializers.Serializer):
|
||
"""Сводка по последней загрузке источника."""
|
||
|
||
batch_id = serializers.IntegerField(read_only=True)
|
||
source = serializers.CharField(read_only=True)
|
||
source_display = serializers.CharField(read_only=True)
|
||
records_count = serializers.IntegerField(read_only=True)
|
||
status = serializers.CharField(read_only=True)
|
||
error_message = serializers.CharField(read_only=True)
|
||
created_at = serializers.DateTimeField(read_only=True)
|
||
updated_at = serializers.DateTimeField(read_only=True)
|
||
|
||
|
||
class SourceCardTaskSerializer(serializers.Serializer):
|
||
"""Краткая информация по фоновой задаче карточки."""
|
||
|
||
task_id = serializers.CharField(read_only=True)
|
||
task_name = serializers.CharField(read_only=True)
|
||
status = serializers.CharField(read_only=True)
|
||
progress = serializers.IntegerField(read_only=True)
|
||
progress_message = serializers.CharField(read_only=True)
|
||
started_at = serializers.DateTimeField(read_only=True)
|
||
created_at = serializers.DateTimeField(read_only=True)
|
||
meta = serializers.JSONField(read_only=True)
|
||
|
||
|
||
class SourceCardItemSerializer(serializers.Serializer):
|
||
"""Подисточник внутри агрегированной карточки."""
|
||
|
||
code = serializers.CharField(read_only=True)
|
||
title = serializers.CharField(read_only=True)
|
||
description = serializers.CharField(read_only=True)
|
||
parser_source = serializers.CharField(read_only=True, allow_null=True)
|
||
parser_source_display = serializers.CharField(read_only=True, allow_null=True)
|
||
records_count = serializers.IntegerField(read_only=True)
|
||
organizations_count = serializers.IntegerField(read_only=True)
|
||
last_updated_at = serializers.DateTimeField(read_only=True, allow_null=True)
|
||
latest_load = SourceCardLoadSerializer(read_only=True, allow_null=True)
|
||
latest_success_load = SourceCardLoadSerializer(read_only=True, allow_null=True)
|
||
|
||
|
||
class SourceCardSerializer(serializers.Serializer):
|
||
"""Serializer for frontend source cards list."""
|
||
|
||
slug = serializers.CharField(read_only=True)
|
||
title = serializers.CharField(read_only=True)
|
||
description = serializers.CharField(read_only=True)
|
||
order = serializers.IntegerField(read_only=True)
|
||
is_available = serializers.BooleanField(read_only=True)
|
||
status = serializers.CharField(read_only=True)
|
||
status_label = serializers.CharField(read_only=True)
|
||
progress = serializers.IntegerField(read_only=True)
|
||
records_count = serializers.IntegerField(read_only=True)
|
||
organizations_count = serializers.IntegerField(read_only=True)
|
||
last_updated_at = serializers.DateTimeField(read_only=True, allow_null=True)
|
||
next_update_at = serializers.DateTimeField(read_only=True, allow_null=True)
|
||
error_message = serializers.CharField(read_only=True)
|
||
task_names = serializers.ListField(child=serializers.CharField(), read_only=True)
|
||
refresh_requires_params = serializers.BooleanField(read_only=True)
|
||
refresh_params = SourceCardRefreshParamSerializer(many=True, read_only=True)
|
||
|
||
|
||
class SourceTaskStatusSerializer(serializers.Serializer):
|
||
"""Табличная строка статуса источника для экрана задач парсинга."""
|
||
|
||
row_number = serializers.IntegerField(read_only=True)
|
||
slug = serializers.CharField(read_only=True)
|
||
source = serializers.CharField(read_only=True)
|
||
status = serializers.CharField(read_only=True)
|
||
status_label = serializers.CharField(read_only=True)
|
||
actualized_at = serializers.DateTimeField(read_only=True, allow_null=True)
|
||
next_update_at = serializers.DateTimeField(read_only=True, allow_null=True)
|
||
records_count = serializers.IntegerField(read_only=True)
|
||
organizations_count = serializers.IntegerField(read_only=True)
|
||
progress = serializers.IntegerField(read_only=True)
|
||
error_message = serializers.CharField(read_only=True)
|
||
active_tasks = SourceCardTaskSerializer(many=True, read_only=True)
|
||
|
||
|
||
class SourceCardDetailSerializer(SourceCardSerializer):
|
||
"""Detailed serializer for a single frontend source card."""
|
||
|
||
active_tasks = SourceCardTaskSerializer(many=True, read_only=True)
|
||
source_items = SourceCardItemSerializer(many=True, read_only=True)
|
||
latest_load = SourceCardLoadSerializer(read_only=True, allow_null=True)
|
||
latest_success_load = SourceCardLoadSerializer(read_only=True, allow_null=True)
|
||
|
||
|
||
class SourceCardRefreshRequestSerializer(serializers.Serializer):
|
||
"""Request body for manual card refresh."""
|
||
|
||
params = serializers.DictField(required=False)
|
||
|
||
|
||
class SourceCardRefreshTaskSerializer(serializers.Serializer):
|
||
"""Queued task info after refresh start."""
|
||
|
||
task_id = serializers.CharField(read_only=True)
|
||
task_name = serializers.CharField(read_only=True)
|
||
|
||
|
||
class SourceCardRefreshResponseSerializer(serializers.Serializer):
|
||
"""Response serializer for manual card refresh."""
|
||
|
||
source_card = serializers.CharField(read_only=True)
|
||
status = serializers.CharField(read_only=True)
|
||
requested_at = serializers.DateTimeField(read_only=True)
|
||
tasks = SourceCardRefreshTaskSerializer(many=True, read_only=True)
|