arm-core/api/src/domain/statement_layout.py

67 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Порядок строк ответа `GET …/statement` (P3-17; спека `arm-core` «Ответ `GET …/statement`»). Чистая функция.
Строки `ok` (в том числе достроенные шаблонные и созданные правкой) — по коду, затем `row_index` (`NULL` последними),
затем в порядке входа (вызывающий передаёт строки по `id`). За строкой `ok` источника идут её «вложения» по
`row_index`: строки `detail` с этим родителем и строки `unreadable` / `missing` / `detail` без (известного) родителя,
у которых она — ближайшая предыдущая по `row_index` строка `ok` источника. Вложения без такой строки — в начале.
"""
import bisect
from collections.abc import Hashable, Sequence
from dataclasses import dataclass
__all__ = ["LayoutLine", "nearest_ok_parents", "order_lines"]
OK = "ok"
DETAIL = "detail"
@dataclass(frozen=True)
class LayoutLine:
key: Hashable
row_index: int | None
line_status: str
line_code: str | None
parent_row_index: int | None
def order_lines(lines: Sequence[LayoutLine]) -> list[Hashable]:
"""Ключи строк в порядке ответа."""
ok_lines = [line for line in lines if line.line_status == OK]
others = [line for line in lines if line.line_status != OK]
ok_ordered = sorted(
ok_lines,
key=lambda line: (line.line_code is None, line.line_code or "", line.row_index is None, line.row_index or 0),
)
# строки `ok` источника по `row_index` — адресаты вложений
sources = sorted((line for line in ok_lines if line.row_index is not None), key=lambda line: line.row_index or 0)
source_rows = [line.row_index or 0 for line in sources]
by_row = {line.row_index: line.key for line in sources}
attached: dict[Hashable | None, list[LayoutLine]] = {}
for line in sorted((line for line in others), key=lambda item: (item.row_index is None, item.row_index or 0)):
owner: Hashable | None = None
if line.line_status == DETAIL and line.parent_row_index in by_row:
owner = by_row[line.parent_row_index]
elif line.row_index is not None:
position = bisect.bisect_left(source_rows, line.row_index) - 1
owner = sources[position].key if position >= 0 else None
attached.setdefault(owner, []).append(line)
result: list[Hashable] = [line.key for line in attached.get(None, [])]
for line in ok_ordered:
result.append(line.key)
result.extend(item.key for item in attached.get(line.key, []))
return result
def nearest_ok_parents(lines: Sequence[LayoutLine]) -> dict[Hashable, int | None]:
"""Родитель строк `detail`: `row_index` ближайшей строки `ok` выше по `row_index` (строки без `row_index` —
шаблонные — родителями не бывают); нет такой — `None`. Временная: в части Б заменяется `detail_parents` пакета."""
ok_rows = sorted(line.row_index for line in lines if line.line_status == OK and line.row_index is not None)
parents: dict[Hashable, int | None] = {}
for line in lines:
if line.line_status != DETAIL:
continue
position = bisect.bisect_left(ok_rows, line.row_index) - 1 if line.row_index is not None else -1
parents[line.key] = ok_rows[position] if position >= 0 else None
return parents