Compare commits
2 Commits
f4772d9016
...
feature/ba
| Author | SHA1 | Date | |
|---|---|---|---|
| d0325ec271 | |||
| 4c5ceb74f6 |
@@ -19,19 +19,6 @@ FROM base AS builder
|
||||
|
||||
ARG INSTALL_DEV=false
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
libffi-dev \
|
||||
libxml2-dev \
|
||||
libxslt1-dev \
|
||||
zlib1g-dev \
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY pyproject.toml uv.lock ./
|
||||
|
||||
RUN if [ "${INSTALL_DEV}" = "true" ]; then \
|
||||
@@ -43,17 +30,6 @@ RUN if [ "${INSTALL_DEV}" = "true" ]; then \
|
||||
|
||||
FROM base AS runtime-base
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
libpq5 \
|
||||
libffi8 \
|
||||
libxml2 \
|
||||
libxslt1.1 \
|
||||
zlib1g \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /app/.venv /app/.venv
|
||||
COPY src/ ./src/
|
||||
COPY docker/scripts/ ./docker/scripts/
|
||||
|
||||
@@ -82,7 +82,9 @@ def build_upload_success_payload(
|
||||
report_quarter=report_quarter,
|
||||
)
|
||||
else:
|
||||
payload["report_period_display"] = report_annual_display(report_year=report_year)
|
||||
payload["report_period_display"] = report_annual_display(
|
||||
report_year=report_year
|
||||
)
|
||||
|
||||
if result is not None:
|
||||
payload["result"] = result
|
||||
|
||||
@@ -4,16 +4,16 @@ from apps.core.viewsets import ClassicReadOnlyViewSet
|
||||
from apps.external_data.models import (
|
||||
ArbitrationCase,
|
||||
IndustrialProduct,
|
||||
InformationSecurityRegistryEntry,
|
||||
ProsecutorCheck,
|
||||
PublicProcurement,
|
||||
InformationSecurityRegistryEntry,
|
||||
)
|
||||
from apps.external_data.serializers import (
|
||||
ArbitrationCaseSerializer,
|
||||
CorporationMembershipSerializer,
|
||||
IndustrialProductSerializer,
|
||||
ProsecutorCheckSerializer,
|
||||
PublicProcurementSerializer,
|
||||
CorporationMembershipSerializer,
|
||||
)
|
||||
from django_filters import rest_framework as filters
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
@@ -122,7 +122,9 @@ class ArbitrationCaseViewSet(ClassicReadOnlyViewSet[ArbitrationCase]):
|
||||
class CorporationMembershipViewSet(
|
||||
ClassicReadOnlyViewSet[InformationSecurityRegistryEntry]
|
||||
):
|
||||
queryset = InformationSecurityRegistryEntry.objects.select_related("organization").all()
|
||||
queryset = InformationSecurityRegistryEntry.objects.select_related(
|
||||
"organization"
|
||||
).all()
|
||||
serializer_class = CorporationMembershipSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_class = CorporationMembershipFilter
|
||||
|
||||
@@ -112,7 +112,9 @@ class ArbitrationCase(UUIDPrimaryKeyMixin, TimestampMixin, models.Model):
|
||||
return f"{self.case_number} ({self.organization_id})"
|
||||
|
||||
|
||||
class InformationSecurityRegistryEntry(UUIDPrimaryKeyMixin, TimestampMixin, models.Model):
|
||||
class InformationSecurityRegistryEntry(
|
||||
UUIDPrimaryKeyMixin, TimestampMixin, models.Model
|
||||
):
|
||||
class PresenceStatus(models.TextChoices):
|
||||
PRESENT = "present", _("В реестре")
|
||||
ABSENT = "absent", _("Не в реестре")
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
from apps.external_data.models import (
|
||||
ArbitrationCase,
|
||||
IndustrialProduct,
|
||||
InformationSecurityRegistryEntry,
|
||||
ProsecutorCheck,
|
||||
PublicProcurement,
|
||||
InformationSecurityRegistryEntry,
|
||||
)
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
from apps.external_data.api import (
|
||||
ArbitrationCaseViewSet,
|
||||
CorporationMembershipViewSet,
|
||||
IndustrialProductViewSet,
|
||||
ProsecutorCheckViewSet,
|
||||
PublicProcurementViewSet,
|
||||
CorporationMembershipViewSet,
|
||||
)
|
||||
from django.urls import include, path
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
- FormF2ParseResultSerializer - результат парсинга
|
||||
"""
|
||||
|
||||
from apps.form_2.models import FormF2Record
|
||||
from apps.core.upload_contracts import (
|
||||
UploadQuarterSerializer,
|
||||
UploadParseResultSerializer,
|
||||
UploadQuarterSerializer,
|
||||
)
|
||||
from apps.form_2.models import FormF2Record
|
||||
from apps.organization.serializers import OrganizationSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
- FormF3ParseResultSerializer - результат парсинга
|
||||
"""
|
||||
|
||||
from apps.form_3.models import FormF3Record
|
||||
from apps.core.upload_contracts import (
|
||||
UploadAnnualSerializer,
|
||||
UploadParseResultSerializer,
|
||||
)
|
||||
from apps.form_3.models import FormF3Record
|
||||
from apps.organization.serializers import OrganizationSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Сериализаторы формы Ф-4."""
|
||||
|
||||
from apps.form_4.models import FormF4Record
|
||||
from apps.core.upload_contracts import (
|
||||
UploadHalfYearSerializer,
|
||||
UploadParseResultSerializer,
|
||||
)
|
||||
from apps.form_4.models import FormF4Record
|
||||
from apps.organization.serializers import OrganizationSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Сериализаторы формы Ф-5."""
|
||||
|
||||
from apps.form_5.models import FormF5Record
|
||||
from apps.core.upload_contracts import (
|
||||
UploadAnnualSerializer,
|
||||
UploadParseResultSerializer,
|
||||
)
|
||||
from apps.form_5.models import FormF5Record
|
||||
from apps.organization.serializers import OrganizationSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Сериализаторы формы Ф-6."""
|
||||
|
||||
from apps.form_6.models import FormF6Record
|
||||
from apps.core.upload_contracts import (
|
||||
UploadAnnualSerializer,
|
||||
UploadParseResultSerializer,
|
||||
)
|
||||
from apps.form_6.models import FormF6Record
|
||||
from apps.organization.serializers import OrganizationSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
@@ -56,9 +56,7 @@ def scope_labels(scope_codes: Iterable[str]) -> list[str]:
|
||||
def get_corporation_scope_dictionary() -> list[dict[str, str | int]]:
|
||||
"""Возвращает справочник корпусов для API-словаря."""
|
||||
items: list[dict[str, str | int]] = []
|
||||
for code, sort_order in sorted(
|
||||
SCOPE_SORT_ORDER.items(), key=lambda item: item[1]
|
||||
):
|
||||
for code, sort_order in sorted(SCOPE_SORT_ORDER.items(), key=lambda item: item[1]):
|
||||
label = SCOPE_LABELS.get(code)
|
||||
short_name = SCOPE_SHORT_NAMES.get(code)
|
||||
if not label or not short_name:
|
||||
|
||||
@@ -129,7 +129,6 @@ class OrganizationCatalogBaseSerializer(serializers.ModelSerializer):
|
||||
return SCOPE_LABELS.get(scope_code, "")
|
||||
|
||||
|
||||
|
||||
class OrganizationCatalogListSerializer(OrganizationCatalogBaseSerializer):
|
||||
"""Сериализатор списка организаций для `/api/v1/organizations/`."""
|
||||
|
||||
|
||||
@@ -249,16 +249,19 @@ class AdminUsersManagementView(APIView):
|
||||
return
|
||||
|
||||
latest_jobs: dict[int, BackgroundJob] = {}
|
||||
jobs = BackgroundJobService.get_queryset().filter(
|
||||
user_id__in=user_ids
|
||||
).annotate(
|
||||
_effective_job_ts=Coalesce(
|
||||
F("completed_at"),
|
||||
F("started_at"),
|
||||
F("updated_at"),
|
||||
F("created_at"),
|
||||
jobs = (
|
||||
BackgroundJobService.get_queryset()
|
||||
.filter(user_id__in=user_ids)
|
||||
.annotate(
|
||||
_effective_job_ts=Coalesce(
|
||||
F("completed_at"),
|
||||
F("started_at"),
|
||||
F("updated_at"),
|
||||
F("created_at"),
|
||||
)
|
||||
)
|
||||
).order_by("user_id", "-_effective_job_ts")
|
||||
.order_by("user_id", "-_effective_job_ts")
|
||||
)
|
||||
|
||||
for job in jobs:
|
||||
if job.user_id not in latest_jobs:
|
||||
|
||||
@@ -4,9 +4,9 @@ import factory
|
||||
from apps.external_data.models import (
|
||||
ArbitrationCase,
|
||||
IndustrialProduct,
|
||||
InformationSecurityRegistryEntry,
|
||||
ProsecutorCheck,
|
||||
PublicProcurement,
|
||||
InformationSecurityRegistryEntry,
|
||||
)
|
||||
from faker import Faker
|
||||
|
||||
@@ -71,9 +71,7 @@ class ArbitrationCaseFactory(factory.django.DjangoModelFactory):
|
||||
decision_date = factory.LazyAttribute(lambda _: fake.date_this_year())
|
||||
|
||||
|
||||
class InformationSecurityRegistryEntryFactory(
|
||||
factory.django.DjangoModelFactory
|
||||
):
|
||||
class InformationSecurityRegistryEntryFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = InformationSecurityRegistryEntry
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ from rest_framework.test import APITestCase
|
||||
from tests.apps.external_data.factories import (
|
||||
ArbitrationCaseFactory,
|
||||
IndustrialProductFactory,
|
||||
InformationSecurityRegistryEntryFactory,
|
||||
ProsecutorCheckFactory,
|
||||
PublicProcurementFactory,
|
||||
InformationSecurityRegistryEntryFactory,
|
||||
)
|
||||
from tests.apps.organization.factories import OrganizationFactory
|
||||
from tests.apps.user.factories import UserFactory
|
||||
|
||||
@@ -193,19 +193,18 @@ class FormUploadContractsApiTest(APITestCase):
|
||||
|
||||
def test_upload_processing_error_contract(self):
|
||||
for _, case in self.CASES.items():
|
||||
with self.subTest(form=case["form"]):
|
||||
with patch(
|
||||
case["parse_target"],
|
||||
side_effect=RuntimeError("parse failed"),
|
||||
) as parse_mock:
|
||||
response = self.client.post(
|
||||
case["url"],
|
||||
self._build_payload(case["payload"], file_size=256),
|
||||
format="multipart",
|
||||
)
|
||||
with self.subTest(form=case["form"]), patch(
|
||||
case["parse_target"],
|
||||
side_effect=RuntimeError("parse failed"),
|
||||
) as parse_mock:
|
||||
response = self.client.post(
|
||||
case["url"],
|
||||
self._build_payload(case["payload"], file_size=256),
|
||||
format="multipart",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data["error_code"], "processing_error")
|
||||
self.assertEqual(response.data["error_message"], "parse failed")
|
||||
self.assertEqual(response.data["details"], [])
|
||||
parse_mock.assert_called_once()
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data["error_code"], "processing_error")
|
||||
self.assertEqual(response.data["error_message"], "parse failed")
|
||||
self.assertEqual(response.data["details"], [])
|
||||
parse_mock.assert_called_once()
|
||||
|
||||
@@ -183,9 +183,18 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
self.assertEqual(response.data["insurance_contributions"]["amount"], 302000)
|
||||
self.assertEqual(response.data["organization_id"], str(self.organization.id))
|
||||
self.assertEqual(response.data["report_period"], {"year": 2026, "quarter": 1})
|
||||
self.assertEqual(set(response.data["revenue"]), {"amount", "previous_amount", "delta_percent"})
|
||||
self.assertEqual(set(response.data["net_profit"]), {"amount", "previous_amount", "delta_percent"})
|
||||
self.assertEqual(set(response.data["taxes_paid"]), {"amount", "previous_amount", "delta_percent"})
|
||||
self.assertEqual(
|
||||
set(response.data["revenue"]),
|
||||
{"amount", "previous_amount", "delta_percent"},
|
||||
)
|
||||
self.assertEqual(
|
||||
set(response.data["net_profit"]),
|
||||
{"amount", "previous_amount", "delta_percent"},
|
||||
)
|
||||
self.assertEqual(
|
||||
set(response.data["taxes_paid"]),
|
||||
{"amount", "previous_amount", "delta_percent"},
|
||||
)
|
||||
self.assertEqual(
|
||||
set(response.data["insurance_contributions"]),
|
||||
{"amount", "previous_amount", "delta_percent"},
|
||||
@@ -225,7 +234,9 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
"?report_year=2026&history_years=2"
|
||||
)
|
||||
self.assertEqual(personnel_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(personnel_response.data["organization_id"], str(self.organization.id))
|
||||
self.assertEqual(
|
||||
personnel_response.data["organization_id"], str(self.organization.id)
|
||||
)
|
||||
self.assertEqual(personnel_response.data["report_year"], 2026)
|
||||
self.assertEqual(
|
||||
personnel_response.data["headcount"]["average_employees"],
|
||||
@@ -285,13 +296,21 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
"?frequency=quarterly&price_mode=actual&report_year=2026"
|
||||
)
|
||||
self.assertEqual(products_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(products_response.data["organization_id"], str(self.organization.id))
|
||||
self.assertEqual(
|
||||
products_response.data["organization_id"], str(self.organization.id)
|
||||
)
|
||||
self.assertEqual(products_response.data["report_year"], 2026)
|
||||
self.assertEqual(products_response.data["frequency"], "quarterly")
|
||||
self.assertEqual(products_response.data["price_mode"], "actual")
|
||||
self.assertEqual(products_response.data["summary"]["military_output_amount"], 11000000)
|
||||
self.assertEqual(products_response.data["summary"]["civilian_output_amount"], 7000000)
|
||||
self.assertEqual(products_response.data["summary"]["hightech_output_amount"], 1500000)
|
||||
self.assertEqual(
|
||||
products_response.data["summary"]["military_output_amount"], 11000000
|
||||
)
|
||||
self.assertEqual(
|
||||
products_response.data["summary"]["civilian_output_amount"], 7000000
|
||||
)
|
||||
self.assertEqual(
|
||||
products_response.data["summary"]["hightech_output_amount"], 1500000
|
||||
)
|
||||
self.assertEqual(products_response.data["summary"]["rd_volume_amount"], 900000)
|
||||
self.assertEqual(len(products_response.data["production_series"]), 1)
|
||||
self.assertEqual(len(products_response.data["sales_series"]), 1)
|
||||
|
||||
Reference in New Issue
Block a user