Files
kadastr18/priem_register/readers/base.py
jze9 b5f7060a1f Приём документов → реестр + журнал
Внесение данных из файлов приёма в реестр невостребованных документов
и заполнение журнала. Дата внесения — текущая; журнал заполняется чисто
(без строк-примеров шаблона).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 03:11:18 +05:00

87 lines
3.9 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.
# -*- coding: utf-8 -*-
"""Базовый класс читателя источника."""
from __future__ import annotations
import abc
import datetime
from pathlib import Path
from typing import List
from ..config import REGISTER_COLUMNS, SOURCE_NUMBER_COL
from ..models import CellValue, ReadResult, Record
# Позиция колонки «Примечание» (L) в раскладке реестра (A=0 … M=12).
NOTE_COL = 11
class SourceReader(abc.ABC):
"""Читает файл приёма и возвращает записи для внесения.
По заданию: «внести все строки, начиная со второй». Поэтому пропускается
только шапка (строка 1), а каждая последующая непустая строка вносится как
отдельная запись — без фильтрации по № п/п. № п/п в реестре назначается
заново (сквозной), исходный № отбрасывается.
В реестре 13 колонок (A..M). Если в источнике есть данные правее колонки M
(например, безымянная 14-я колонка), переносить их в отдельную колонку
некуда — они добавляются в «Примечание» той же строки, а факт фиксируется
в ``notes`` (информационно).
Подклассы реализуют только :meth:`_rows` — выдачу «сырых» строк таблицы
(список типизированных значений ячеек) первого листа.
"""
def __init__(self, path: Path):
self.path = Path(path)
@abc.abstractmethod
def _rows(self) -> List[List[CellValue]]:
"""Сырые строки первого листа, типизированные значения ячеек."""
def read(self) -> ReadResult:
result = ReadResult()
for index, row in enumerate(self._rows()):
if index == 0:
continue # шапка (строка 1)
if not self._has_content(row):
continue # пустые строки не вносим
record = Record(values=list(row[SOURCE_NUMBER_COL + 1: REGISTER_COLUMNS]))
self._fold_overflow(row, record, result, index + 1)
result.records.append(record)
return result
def _fold_overflow(self, row: List[CellValue], record: Record,
result: ReadResult, human_row: int) -> None:
overflow = [v for i, v in enumerate(row)
if i >= REGISTER_COLUMNS and v not in (None, "")]
for value in overflow:
_merge_cell(record, NOTE_COL, value)
if overflow:
result.notes.append(
"строка %d: данные за колонкой M добавлены в «Примечание»: %s"
% (human_row, ", ".join(_as_text(v) for v in overflow)))
@staticmethod
def _has_content(row: List[CellValue]) -> bool:
return any(v not in (None, "") for v in row)
def _merge_cell(record: Record, position: int, value: CellValue) -> None:
"""Записать значение в колонку записи; занятое поле дополнить через «; »."""
idx = position - 1
while len(record.values) <= idx:
record.values.append(None)
current = record.values[idx]
if current in (None, ""):
record.values[idx] = value
else:
record.values[idx] = "%s; %s" % (_as_text(current), _as_text(value))
def _as_text(value: CellValue) -> str:
if isinstance(value, datetime.date): # покрывает и datetime
return value.strftime("%d.%m.%Y")
if isinstance(value, float) and value.is_integer():
return str(int(value))
return str(value)