fix(parsers): align vacancy sources and procurement counters
All checks were successful
CI/CD Pipeline / Quality Gate (push) Successful in 29s
CI/CD Pipeline / Build and Push Images (push) Successful in 10s
CI/CD Pipeline / Internal Notify (push) Successful in 0s
CI/CD Pipeline / Deploy Dev in Dokploy (push) Successful in 1s

This commit is contained in:
2026-05-14 14:45:58 +02:00
parent 89607356b7
commit 6d1ec2e55c
12 changed files with 340 additions and 57 deletions

View File

@@ -19,6 +19,7 @@ from zipfile import ZIP_DEFLATED, ZipFile
import requests
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
GenericParserRecord,
IndustrialProductRecord,
InspectionRecord,
@@ -583,7 +584,7 @@ class StateCorpExchangeService:
items: list[dict[str, str | None]] = []
for record in cls._generic_records(
allowed_inns,
sources=[ParserLoadLog.Source.TRUDVSEM],
sources=list(VACANCY_RECORD_SOURCES),
):
payload = cls._record_payload(record)
published_at = cls._coerce_date(

View File

@@ -0,0 +1,42 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("parsers", "0022_seed_daily_registry_enrichment_schedule"),
]
operations = [
migrations.AlterField(
model_name="genericparserrecord",
name="source",
field=models.CharField(
choices=[
(
"industrial",
"Сертификаты промышленного производства",
),
("industrial_products", "Реестр промышленной продукции"),
("manufactures", "Реестр производителей"),
("inspections", "Единый реестр проверок"),
("procurements", "Единая информационная система закупок"),
("fns_reports", "Бухгалтерская отчетность ФНС"),
("procurements_44fz", "Закупки 44-ФЗ"),
("procurements_223fz", "Закупки 223-ФЗ"),
("contracts", "Контракты ЕИС"),
("unfair_suppliers", "Недобросовестные поставщики"),
("fas_goz", "Уклонение от ГОЗ"),
("arbitration", "Арбитражные дела"),
("fedresurs_bankruptcy", "Банкротства Федресурс"),
("fstec", "Реестры ФСТЭК"),
("trudvsem", "Вакансии Работа России"),
("hh", "Вакансии HeadHunter"),
("superjob", "Вакансии SuperJob"),
],
db_index=True,
help_text="Источник данных",
max_length=50,
verbose_name="источник",
),
),
]

View File

@@ -8,6 +8,8 @@ from apps.core.mixins import TimestampMixin
from django.db import models
from django.utils.translation import gettext_lazy as _
VACANCY_RECORD_SOURCES = ("trudvsem", "hh", "superjob")
class ParserLoadLog(TimestampMixin, models.Model):
"""
@@ -115,6 +117,13 @@ class ParserBatchSequence(TimestampMixin, models.Model):
return f"{self.source}: next batch {self.next_batch_id}"
GENERIC_RECORD_SOURCE_CHOICES = [
*ParserLoadLog.Source.choices,
("hh", _("Вакансии HeadHunter")),
("superjob", _("Вакансии SuperJob")),
]
class IndustrialCertificateRecord(TimestampMixin, models.Model):
"""
Сертификат промышленного производства РФ.
@@ -369,7 +378,7 @@ class GenericParserRecord(TimestampMixin, models.Model):
source = models.CharField(
_("источник"),
max_length=50,
choices=ParserLoadLog.Source.choices,
choices=GENERIC_RECORD_SOURCE_CHOICES,
db_index=True,
help_text=_("Источник данных"),
)

View File

@@ -850,6 +850,7 @@ class ParserResultRecordSerializer(serializers.Serializer):
id = serializers.IntegerField()
load_batch = serializers.IntegerField()
source = serializers.CharField()
vacancy_source = serializers.CharField(allow_blank=True, required=False)
external_id = serializers.CharField(allow_blank=True)
inn = serializers.CharField(allow_blank=True)
ogrn = serializers.CharField(allow_blank=True)

View File

@@ -25,6 +25,7 @@ from apps.parsers.clients.proverki.schemas import Inspection
from apps.parsers.clients.proxy_tools import ProxyToolsClient, ProxyToolsClientError
from apps.parsers.clients.zakupki.schemas import Procurement
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
FinancialReport,
FinancialReportLine,
GenericParserRecord,
@@ -780,6 +781,23 @@ class GenericParserRecordService(BulkOperationsMixin, BaseService[GenericParserR
"""Сервис для универсальных записей новых источников."""
model = GenericParserRecord
vacancy_record_sources = set(VACANCY_RECORD_SOURCES)
@classmethod
def _storage_source_for_record(
cls,
record: GenericParserItem,
*,
default_source: str,
) -> str:
"""Return DB source for a record without changing the parser load source."""
if default_source != ParserLoadLog.Source.TRUDVSEM:
return default_source
payload = record.payload if isinstance(record.payload, dict) else {}
vacancy_source = str(payload.get("vacancy_source") or "").strip()
if vacancy_source in cls.vacancy_record_sources:
return vacancy_source
return default_source
@classmethod
def _create_with_exact_count(
@@ -837,15 +855,21 @@ class GenericParserRecordService(BulkOperationsMixin, BaseService[GenericParserR
logger.warning("No generic parser records to save (source=%s)", source)
return 0
unique_records = {}
unique_records: dict[tuple[str, str], GenericParserItem] = {}
for record in records:
unique_records.setdefault(record.external_id, record)
record_source = cls._storage_source_for_record(
record,
default_source=source,
)
unique_records.setdefault((record_source, record.external_id), record)
existing_external_ids = set(
source_values = {record_source for record_source, _ in unique_records}
external_ids = {external_id for _, external_id in unique_records}
existing_keys = set(
cls.model.objects.filter(
source=source,
external_id__in=unique_records.keys(),
).values_list("external_id", flat=True)
source__in=source_values,
external_id__in=external_ids,
).values_list("source", "external_id")
)
registry_lookup = RegistryOrganizationResolver.build_lookup(
[(record.inn, record.ogrn) for record in unique_records.values()]
@@ -853,7 +877,7 @@ class GenericParserRecordService(BulkOperationsMixin, BaseService[GenericParserR
instances = [
cls.model(
load_batch=batch_id,
source=source,
source=record_source,
external_id=record.external_id,
inn=record.inn,
ogrn=record.ogrn,
@@ -870,8 +894,8 @@ class GenericParserRecordService(BulkOperationsMixin, BaseService[GenericParserR
ogrn=record.ogrn,
),
)
for external_id, record in unique_records.items()
if external_id not in existing_external_ids
for (record_source, external_id), record in unique_records.items()
if (record_source, external_id) not in existing_keys
]
if not instances:
logger.info("No new generic records to save (source=%s)", source)

View File

@@ -11,6 +11,7 @@ from typing import Any
from apps.core.models import JobStatus
from apps.core.services import BackgroundJobService
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
FinancialReport,
FinancialReportLine,
GenericParserRecord,
@@ -22,7 +23,8 @@ from apps.parsers.models import (
ProcurementRecord,
)
from django.conf import settings
from django.db.models import Max, Q
from django.db.models import CharField, F, Max, Q, Value
from django.db.models.functions import Coalesce, NullIf
from django.http import Http404
from django.utils import timezone
from rest_framework.exceptions import ValidationError
@@ -326,6 +328,11 @@ GENERIC_RECORD_SOURCES_BY_ITEM_CODE = {
"fstec": ParserLoadLog.Source.FSTEC,
"trudvsem": ParserLoadLog.Source.TRUDVSEM,
}
PROCUREMENT_BUYER_ITEM_CODES = {
"procurements_44fz",
"procurements_223fz",
"contracts",
}
class SourceCardService:
@@ -772,9 +779,11 @@ class SourceCardService:
@classmethod
def _get_source_records_count(cls, item_code: str) -> int:
generic_source = GENERIC_RECORD_SOURCES_BY_ITEM_CODE.get(item_code)
if generic_source:
return GenericParserRecord.objects.filter(source=generic_source).count()
generic_sources = cls._get_generic_sources_for_item_code(item_code)
if generic_sources:
return GenericParserRecord.objects.filter(
source__in=generic_sources
).count()
if item_code == "fns_reports":
return FinancialReportLine.objects.count()
if item_code == "industrial":
@@ -791,10 +800,14 @@ class SourceCardService:
@classmethod
def _get_source_organizations_count(cls, item_code: str) -> int:
generic_source = GENERIC_RECORD_SOURCES_BY_ITEM_CODE.get(item_code)
if generic_source:
generic_sources = cls._get_generic_sources_for_item_code(item_code)
if generic_sources:
if item_code in PROCUREMENT_BUYER_ITEM_CODES:
return cls._get_generic_procurement_buyer_identities(
generic_sources
).count()
return (
GenericParserRecord.objects.filter(source=generic_source)
GenericParserRecord.objects.filter(source__in=generic_sources)
.exclude(inn="")
.values("inn")
.distinct()
@@ -846,11 +859,11 @@ class SourceCardService:
@classmethod
def _get_source_data_timestamp(cls, item_code: str):
generic_source = GENERIC_RECORD_SOURCES_BY_ITEM_CODE.get(item_code)
if generic_source:
return GenericParserRecord.objects.filter(source=generic_source).aggregate(
last_updated=Max("updated_at")
)["last_updated"]
generic_sources = cls._get_generic_sources_for_item_code(item_code)
if generic_sources:
return GenericParserRecord.objects.filter(
source__in=generic_sources
).aggregate(last_updated=Max("updated_at"))["last_updated"]
if item_code == "fns_reports":
return FinancialReport.objects.aggregate(last_updated=Max("updated_at"))[
"last_updated"
@@ -887,16 +900,13 @@ class SourceCardService:
generic_sources = cls._get_generic_sources_for_definition(definition)
legacy_inns = (
ProcurementRecord.objects.exclude(customer_inn="")
.annotate(buyer_identity=F("customer_inn"))
.order_by()
.values_list("customer_inn", flat=True)
.values_list("buyer_identity", flat=True)
.distinct()
)
generic_inns = (
GenericParserRecord.objects.filter(source__in=generic_sources)
.exclude(inn="")
.order_by()
.values_list("inn", flat=True)
.distinct()
generic_inns = cls._get_generic_procurement_buyer_identities(
generic_sources
)
return legacy_inns.union(generic_inns).count()
@@ -932,15 +942,44 @@ class SourceCardService:
)
return industrial_inns.union(manufacturer_inns, product_inns).count()
@staticmethod
def _get_generic_sources_for_item_code(item_code: str) -> list[str]:
generic_source = GENERIC_RECORD_SOURCES_BY_ITEM_CODE.get(item_code)
if not generic_source:
return []
if generic_source == ParserLoadLog.Source.TRUDVSEM:
return list(VACANCY_RECORD_SOURCES)
return [generic_source]
@staticmethod
def _get_generic_procurement_buyer_identities(generic_sources: list[str]):
return (
GenericParserRecord.objects.filter(source__in=generic_sources)
.annotate(
buyer_identity=Coalesce(
NullIf(F("inn"), Value("")),
NullIf(F("organisation_name"), Value("")),
output_field=CharField(),
)
)
.exclude(buyer_identity__isnull=True)
.order_by()
.values_list("buyer_identity", flat=True)
.distinct()
)
@staticmethod
def _get_generic_sources_for_definition(
definition: SourceCardDefinition,
) -> list[str]:
return list(
dict.fromkeys(
GENERIC_RECORD_SOURCES_BY_ITEM_CODE[item.code]
source
for item in definition.source_items
if item.code in GENERIC_RECORD_SOURCES_BY_ITEM_CODE
for source in SourceCardService._get_generic_sources_for_item_code(
item.code
)
)
)

View File

@@ -17,6 +17,7 @@ from apps.core.services import BackgroundJobService
from apps.parsers import tasks
from apps.parsers.fns_upload import FNSUploadService
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
FinancialReport,
GenericParserRecord,
IndustrialCertificateRecord,
@@ -157,6 +158,7 @@ class MultipartFormSwaggerAutoSchema(SwaggerAutoSchema):
PARSER_TASK_NAMES = set(TASKS_BY_NAME)
VACANCY_RESULT_SOURCES = set(VACANCY_RECORD_SOURCES)
NATIVE_RECORD_MODELS = {
ParserLoadLog.Source.INDUSTRIAL: IndustrialCertificateRecord,
ParserLoadLog.Source.INDUSTRIAL_PRODUCTS: IndustrialProductRecord,
@@ -1554,10 +1556,23 @@ def _generic_record_to_result(
*,
include_payload: bool = True,
) -> dict:
response_source = record.source
vacancy_source = ""
if record.source in VACANCY_RESULT_SOURCES:
payload = record.payload if isinstance(record.payload, dict) else {}
payload_source = str(payload.get("vacancy_source") or "").strip()
vacancy_source = (
payload_source
if payload_source in VACANCY_RESULT_SOURCES
else record.source
)
response_source = vacancy_source
return {
"id": record.id,
"load_batch": record.load_batch,
"source": record.source,
"source": response_source,
"vacancy_source": vacancy_source,
"external_id": record.external_id,
"inn": record.inn,
"ogrn": record.ogrn,
@@ -2208,6 +2223,8 @@ def _apply_native_search(queryset, source: str, search: str):
def _route_model_sources(descriptor) -> set[str]:
if descriptor.source == ParserLoadLog.Source.TRUDVSEM:
return set(VACANCY_RECORD_SOURCES)
return {
item.source
for item in PARSER_SOURCES.values()
@@ -2220,6 +2237,11 @@ def _result_sources_for_request(descriptor, params: dict) -> set[str]:
requested_source = params.get("source")
if not requested_source:
return route_sources
if (
descriptor.source == ParserLoadLog.Source.TRUDVSEM
and requested_source in VACANCY_RESULT_SOURCES
):
return route_sources
if requested_source in route_sources:
return {requested_source}
requested_descriptor = PARSER_SOURCES.get(requested_source)
@@ -2252,10 +2274,27 @@ def _filter_native_result_queryset(source: str, params: dict, sources: set[str])
return queryset.order_by(*(ordering or ["-created_at"]))
def _filter_generic_result_queryset(sources: set[str], params: dict):
def _filter_generic_result_queryset(
sources: set[str],
params: dict,
*,
route_source: str,
):
queryset = GenericParserRecord.objects.filter(source__in=sources)
if not sources:
queryset = queryset.none()
requested_source = params.get("source")
if (
route_source == ParserLoadLog.Source.TRUDVSEM
and requested_source in VACANCY_RESULT_SOURCES
):
if requested_source == ParserLoadLog.Source.TRUDVSEM:
queryset = queryset.filter(
Q(payload__vacancy_source=ParserLoadLog.Source.TRUDVSEM)
| ~Q(payload__has_key="vacancy_source")
)
else:
queryset = queryset.filter(payload__vacancy_source=requested_source)
for field in ("id", "external_id", "inn", "ogrn", "load_batch", "status"):
value = params.get(field)
if value not in ("", None):
@@ -2295,7 +2334,11 @@ def _filter_result_queryset(source_key: str, params: dict):
source = descriptor.source
if source in NATIVE_RECORD_MODELS:
return descriptor, _filter_native_result_queryset(source, params, sources)
return descriptor, _filter_generic_result_queryset(sources, params)
return descriptor, _filter_generic_result_queryset(
sources,
params,
route_source=source,
)
def _result_record_to_dict(source: str, record, *, include_payload: bool) -> dict:

View File

@@ -7,6 +7,7 @@ from datetime import date, datetime
from typing import Any
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
FinancialReport,
FinancialReportLine,
GenericParserRecord,
@@ -148,6 +149,12 @@ def _source_matches(source: str) -> dict[str, set[str]]:
FinancialReport.objects.values_list("ogrn", flat=True).distinct()
),
}
if source == ParserLoadLog.Source.TRUDVSEM:
return OrganizationApiEnrichmentService._matching_identifiers_for_all(
GenericParserRecord.objects.filter(source__in=VACANCY_RECORD_SOURCES),
inn_field="inn",
ogrn_field="ogrn",
)
if source in GENERIC_SOURCES:
return OrganizationApiEnrichmentService._matching_identifiers_for_all(
GenericParserRecord.objects.filter(source=source),
@@ -395,6 +402,24 @@ class OrganizationApiEnrichmentService:
items.append(item)
presence[str(organization.uid)][to_api_data_source(source)] = items
@staticmethod
def _generic_query_sources(
selected_sources: list[str],
) -> tuple[list[str], dict[str, str]]:
query_sources: list[str] = []
source_bucket_by_record_source: dict[str, str] = {}
for source in selected_sources:
source_key = str(source)
expanded_sources = (
VACANCY_RECORD_SOURCES
if source == ParserLoadLog.Source.TRUDVSEM
else (source_key,)
)
for expanded_source in expanded_sources:
query_sources.append(str(expanded_source))
source_bucket_by_record_source[str(expanded_source)] = source_key
return query_sources, source_bucket_by_record_source
@classmethod
def _attach_generic_records(
cls,
@@ -411,6 +436,10 @@ class OrganizationApiEnrichmentService:
if identity_filter is None:
return
query_sources, source_bucket_by_record_source = cls._generic_query_sources(
selected_sources
)
records_by_source_and_inn: dict[str, dict[str, list[dict[str, Any]]]] = {
str(source): {} for source in selected_sources
}
@@ -419,13 +448,13 @@ class OrganizationApiEnrichmentService:
}
records = (
GenericParserRecord.objects.filter(source__in=selected_sources)
GenericParserRecord.objects.filter(source__in=query_sources)
.filter(identity_filter)
.order_by("source", "-created_at", "-id")
)
for record in records:
item = cls._serialize_generic_record(record)
source = str(record.source)
source = source_bucket_by_record_source[str(record.source)]
if record.inn:
records_by_source_and_inn[source].setdefault(record.inn, []).append(
item

View File

@@ -7,6 +7,7 @@ from collections.abc import Iterable
from dataclasses import dataclass
from apps.parsers.models import (
VACANCY_RECORD_SOURCES,
FinancialReport,
GenericParserRecord,
IndustrialCertificateRecord,
@@ -273,8 +274,16 @@ class OrganizationDataSnapshotRefreshService:
ParserLoadLog.Source.FSTEC,
ParserLoadLog.Source.TRUDVSEM,
}:
sources = (
VACANCY_RECORD_SOURCES
if source == ParserLoadLog.Source.TRUDVSEM
else (source,)
)
return _identity_values(
GenericParserRecord.objects.filter(source=source, load_batch=batch_id),
GenericParserRecord.objects.filter(
source__in=sources,
load_batch=batch_id,
),
inn_field="inn",
ogrn_field="ogrn",
)

View File

@@ -434,6 +434,44 @@ class SourceCardServiceDatabaseTest(TestCase):
self.assertEqual(card["records_count"], 2)
self.assertEqual(card["organizations_count"], 1)
def test_public_procurements_counts_generic_buyers_without_inn(self):
GenericParserRecord.objects.create(
source=ParserLoadLog.Source.PROCUREMENTS_44FZ,
load_batch=1,
external_id="notice-1",
inn="",
organisation_name="ГБУ Заказчик",
title="Закупка 44-ФЗ",
payload={"Заказчик": "ГБУ Заказчик"},
)
GenericParserRecord.objects.create(
source=ParserLoadLog.Source.CONTRACTS,
load_batch=1,
external_id="contract-1",
inn="",
organisation_name="ГБУ Заказчик",
title="Контракт ЕИС",
payload={"Заказчик": "ГБУ Заказчик"},
)
GenericParserRecord.objects.create(
source=ParserLoadLog.Source.PROCUREMENTS_223FZ,
load_batch=1,
external_id="notice-2",
inn="",
organisation_name="АО Другой заказчик",
title="Закупка 223-ФЗ",
payload={"Наименование заказчика": "АО Другой заказчик"},
)
card = SourceCardService.get_card("public-procurements")
self.assertEqual(card["records_count"], 3)
self.assertEqual(card["organizations_count"], 2)
source_items = {item["code"]: item for item in card["source_items"]}
self.assertEqual(source_items["procurements_44fz"]["organizations_count"], 1)
self.assertEqual(source_items["procurements_223fz"]["organizations_count"], 1)
self.assertEqual(source_items["contracts"]["organizations_count"], 1)
def test_get_active_tasks_ignores_old_jobs_even_when_updated_recently(self):
job = BackgroundJob.objects.create(
task_id="old-source-task",

View File

@@ -2253,11 +2253,16 @@ class ParseVacanciesTaskTestCase(TestCase):
self.assertEqual(captured_fetch_kwargs["text"], "инженер")
self.assertEqual(
set(
GenericParserRecord.objects.filter(
source=ParserLoadLog.Source.TRUDVSEM
).values_list("external_id", flat=True)
GenericParserRecord.objects.values_list(
"external_id",
"source",
)
),
{"trudvsem:1", "hh:1", "superjob:1"},
{
("trudvsem:1", "trudvsem"),
("hh:1", "hh"),
("superjob:1", "superjob"),
},
)

View File

@@ -353,6 +353,54 @@ class ParsersViewSetTest(APITestCase):
self.assertEqual(detail_response.status_code, status.HTTP_200_OK)
self.assertEqual(detail_response.data["data"]["payload"], {"registry": "44fz"})
def test_vacancy_result_endpoint_uses_job_board_source_as_source(self):
generic_record = GenericParserRecord.objects.create(
load_batch=1,
source="hh",
external_id="hh:123",
title="HeadHunter vacancy",
payload={"vacancy_source": "hh"},
)
self.client.force_authenticate(self.user)
response = self.client.get("/api/v1/trudvsem/vacancies/")
detail_response = self.client.get(
f"/api/v1/trudvsem/vacancies/{generic_record.id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(detail_response.status_code, status.HTTP_200_OK)
row = response.data["data"][0]
detail = detail_response.data["data"]
self.assertEqual(row["source"], "hh")
self.assertEqual(row["vacancy_source"], "hh")
self.assertEqual(detail["source"], "hh")
self.assertEqual(detail["vacancy_source"], "hh")
def test_vacancy_result_endpoint_filters_by_job_board_source(self):
hh_record = GenericParserRecord.objects.create(
load_batch=1,
source="hh",
external_id="hh:123",
title="HeadHunter vacancy",
payload={"vacancy_source": "hh"},
)
GenericParserRecord.objects.create(
load_batch=1,
source="superjob",
external_id="superjob:456",
title="SuperJob vacancy",
payload={"vacancy_source": "superjob"},
)
self.client.force_authenticate(self.user)
response = self.client.get("/api/v1/trudvsem/vacancies/", {"source": "hh"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["meta"]["pagination"]["count"], 1)
self.assertEqual(response.data["data"][0]["id"], hh_record.id)
self.assertEqual(response.data["data"][0]["source"], "hh")
def test_parser_results_v1_enrich_missing_organization_fields_without_contract_change(
self,
):
@@ -490,8 +538,12 @@ class ParsersViewSetTest(APITestCase):
coverage = response.data["data"]["registry_data_coverage"]
self.assertEqual(coverage["total_organizations"], 1)
items = {item["source"]: item for item in coverage["items"]}
self.assertEqual(items[ParserLoadLog.Source.FNS_REPORTS]["organizations_count"], 1)
self.assertEqual(items[ParserLoadLog.Source.FNS_REPORTS]["coverage_percent"], 100)
self.assertEqual(
items[ParserLoadLog.Source.FNS_REPORTS]["organizations_count"], 1
)
self.assertEqual(
items[ParserLoadLog.Source.FNS_REPORTS]["coverage_percent"], 100
)
self.assertNotIn(ParserLoadLog.Source.UNFAIR_SUPPLIERS, items)
def test_dashboard_data_exposes_registry_enrichment_analytics(self):
@@ -559,15 +611,11 @@ class ParsersViewSetTest(APITestCase):
item["source"]: item for item in analytics["source_coverage"]
}
self.assertEqual(
source_coverage[ParserLoadLog.Source.FNS_REPORTS][
"organizations_count"
],
source_coverage[ParserLoadLog.Source.FNS_REPORTS]["organizations_count"],
1,
)
self.assertEqual(
source_coverage[ParserLoadLog.Source.INDUSTRIAL][
"organizations_count"
],
source_coverage[ParserLoadLog.Source.INDUSTRIAL]["organizations_count"],
1,
)
self.assertNotIn(ParserLoadLog.Source.UNFAIR_SUPPLIERS, source_coverage)
@@ -575,9 +623,7 @@ class ParsersViewSetTest(APITestCase):
self.assertNotIn(ParserLoadLog.Source.INSPECTIONS, source_coverage)
risk_signals = {item["source"]: item for item in analytics["risk_signals"]}
self.assertEqual(
risk_signals[ParserLoadLog.Source.UNFAIR_SUPPLIERS][
"organizations_count"
],
risk_signals[ParserLoadLog.Source.UNFAIR_SUPPLIERS]["organizations_count"],
1,
)
self.assertEqual(
@@ -585,9 +631,7 @@ class ParsersViewSetTest(APITestCase):
"critical",
)
self.assertEqual(
risk_signals[ParserLoadLog.Source.FEDRESURS_BANKRUPTCY][
"risk_severity"
],
risk_signals[ParserLoadLog.Source.FEDRESURS_BANKRUPTCY]["risk_severity"],
"critical",
)
self.assertEqual(
@@ -603,8 +647,7 @@ class ParsersViewSetTest(APITestCase):
1,
)
matrix = {
item["registry_id"]: item
for item in analytics["registry_source_matrix"]
item["registry_id"]: item for item in analytics["registry_source_matrix"]
}
rosatom_matrix = matrix[str(rosatom_membership.registry_id)]
roscosmos_matrix = matrix[str(roscosmos_membership.registry_id)]