From 4482179e8281b72c6da0a799e1d07629ded6a53f Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 7 Jul 2026 10:04:09 +0300 Subject: [PATCH] optimization --- app/pipeline/mapping.py | 39 ++++++++++++++++++++++++++++---------- app/pipeline/service.py | 14 ++++++++++++++ tests/unit/test_mapping.py | 28 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/app/pipeline/mapping.py b/app/pipeline/mapping.py index d326ad4..1b41d94 100644 --- a/app/pipeline/mapping.py +++ b/app/pipeline/mapping.py @@ -153,11 +153,20 @@ def build_fixed_row( "Уникальный номер операции": str(operation_index), } merged = _merge_fields(operation_fields, participant_fields) + merged_suffix_index = _build_suffix_index(merged) + participant_suffix_index = _build_suffix_index(participant_fields) for rule in REPORT_MAPPING_RULES: if row.get(rule.column_name): continue - source_payload = participant_fields if rule.source == "participant" else merged - row[rule.column_name] = _pick_value(source_payload, rule.candidate_keys) + if rule.source == "participant": + source_payload = participant_fields + suffix_index = participant_suffix_index + else: + source_payload = merged + suffix_index = merged_suffix_index + row[rule.column_name] = _pick_value( + source_payload, suffix_index, rule.candidate_keys + ) for column in FIXED_REPORT_COLUMNS: row.setdefault(column, "") return row @@ -173,17 +182,27 @@ def _merge_fields( return merged -def _pick_value(payload: dict[str, str], candidates: tuple[str, ...]) -> str: +def _build_suffix_index(payload: dict[str, str]) -> dict[str, str]: + index: dict[str, str] = {} + for key, value in payload.items(): + if not value: + continue + short = key.rsplit(".", maxsplit=1)[-1] + index.setdefault(short, value) + return index + + +def _pick_value( + payload: dict[str, str], + suffix_index: dict[str, str], + candidates: tuple[str, ...], +) -> str: for key in candidates: if key in payload and payload[key]: return payload[key] - short = key.split(".")[-1] + short = key.rsplit(".", maxsplit=1)[-1] if short in payload and payload[short]: return payload[short] - dotted_suffix = f".{short}" - for payload_key, payload_value in payload.items(): - if not payload_value: - continue - if payload_key.endswith(dotted_suffix): - return payload_value + if short in suffix_index: + return suffix_index[short] return "" diff --git a/app/pipeline/service.py b/app/pipeline/service.py index 2a8c9c7..9620c25 100644 --- a/app/pipeline/service.py +++ b/app/pipeline/service.py @@ -1,6 +1,7 @@ from __future__ import annotations import tempfile +import time from dataclasses import dataclass, field from datetime import datetime from pathlib import Path @@ -39,6 +40,7 @@ class BatchResult: protocol_path: str log_path: str messages: list[str] = field(default_factory=list) + duration_seconds: float = 0.0 def run_batch( @@ -48,6 +50,7 @@ def run_batch( samba_password: str, launcher: str, ) -> BatchResult: + started_at = time.perf_counter() messages: list[str] = [] if not samba_user: return BatchResult( @@ -195,6 +198,8 @@ def run_batch( status=status, processed=processed, partial=partial, errors=errors ) ) + duration_seconds = round(time.perf_counter() - started_at, 2) + messages.append(f"Время выполнения: {_format_duration(duration_seconds)}.") messages.extend(log_lines[-5:]) return BatchResult( status=status, @@ -205,6 +210,7 @@ def run_batch( protocol_path=protocol_path, log_path=log_path, messages=messages, + duration_seconds=duration_seconds, ) @@ -362,3 +368,11 @@ def _build_result_message( f"успешно={processed}, частично={partial}, с ошибками={errors}." ) return f"Ошибка обработки: успешно={processed}, частично={partial}, с ошибками={errors}." + + +def _format_duration(duration_seconds: float) -> str: + total_seconds = max(0, int(round(duration_seconds))) + minutes, seconds = divmod(total_seconds, 60) + if minutes: + return f"{minutes} мин {seconds} сек" + return f"{seconds} сек" diff --git a/tests/unit/test_mapping.py b/tests/unit/test_mapping.py index 6507c04..753c183 100644 --- a/tests/unit/test_mapping.py +++ b/tests/unit/test_mapping.py @@ -121,3 +121,31 @@ def test_extract_tag_candidate_keys_supports_comma_separator() -> None: "КППЮЛ, ИдентификацияФЛ, ПризнакОргФормаИНБОЮЛ" ) assert candidates == ("КППЮЛ", "ИдентификацияФЛ", "ПризнакОргФормаИНБОЮЛ") + + +def test_build_fixed_row_uses_first_suffix_value_from_index() -> None: + row = build_fixed_row( + file_name="f.xml", + record_id="R1", + operation_index=1, + operation_fields={}, + participant_fields={ + "УчастникФЛИП.Первый.ИННФЛИП": "111111111111", + "УчастникФЛИП.Второй.ИННФЛИП": "222222222222", + }, + ) + assert row[_column_with("ИНН ")] == "111111111111" + + +def test_build_fixed_row_skips_empty_suffix_values_in_index() -> None: + row = build_fixed_row( + file_name="f.xml", + record_id="R1", + operation_index=1, + operation_fields={}, + participant_fields={ + "УчастникФЛИП.Первый.ИННФЛИП": "", + "УчастникФЛИП.Второй.ИННФЛИП": "222222222222", + }, + ) + assert row[_column_with("ИНН ")] == "222222222222"