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

@@ -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)]