Статья не имеет цели доказать важность и необходимость существования документа «Текст программы» и, тем более, необходимость его разработки в виде документа, которым кто-то когда-либо воспользуется. Цель статьи показать основы автоматизации обработки документов формата *.doc (*.docx) с использованием скриптов, написанных на языке Python.

Документ «Текст программы» (далее – Документ) входит в состав комплекта программных документов, перечень которых определен ГОСТ 19.101-77. ГОСТ, бесспорно, достаточно старый, однако его требования до сих пор востребованы при разработке программной продукции, соответственно требуется и разработка рассматриваемого Документа.

Содержание Документа определено ГОСТ 19.401-78 и должно включать в себя либо символическую запись на исходном языке, либо символическую запись на промежуточных языках, либо символическое представление машинных кодов (и т.п.).

Учитывая объемы современных веб приложений, разрабатываемых с использованием нескольких фреймворков, подготовить данный документ без использования автоматизации достаточно сложно.

Возможно два варианта создания документа:

  1. разработчики собирают содержание всех файлов проекта в один текстовый файл и передают техническому писателю на обработку;

  2. разработчики передают техническому писателю архив с файлами проекта, которые технический писатель должен обработать сам.

Первый вариант зависим от загруженности разработчиков. Чтобы сформировать текстовый файл с кодом программы, кто-то из команды разработчиков должен отвлечься, выгрузить код проекта из репозитория, написать программу, которая обработает выгруженный код и выдаст текстовый файл. Файл может быть размером как в пару мегабайт, так и в пару сотен мегабайт, его нужно вставить в Документ и как-то оформить. Вставка такого объема информации в файл Microsoft Word может занять как 30 минут, так и несколько часов. При этом, когда вставка будет завершена, Документ будет не структурирован и не читаем.

Второй вариант зависим от технических навыков исполнителя, но предпочтителен с точки зрения отсутствия необходимости привлечения людей, не имеющих отношения к разработке документации, вследствие чего будем автоматизировать именно этот вариант.

Автоматизация рассматриваемого Документа заключается в том, чтобы перенести текст кода программы в документ с заданным форматированием и структурой, чтобы в дальнейшем им можно было воспользоваться (маловероятный, но имеющий право на жизнь вариант использования).

Файл формата Microsoft Word (*.doc, *.docx) представляет собой набор данных в разметке xml. Файл имеет такие атрибуты, как параграф, рисунок, таблица, стиль. Для доступа к файлу и его атрибутам Microsoft в свое время была разработана технология OLE (Object Linking and Embedding). OLE позволяет передавать часть работы от одной программы редактирования к другой и возвращать результаты назад.

Библиотеки, осуществляющие взаимодействие с файлами офисного пакета Microsoft Office с использованием технологии OLE, есть практически для всех языков программирования. Для Python это библиотека python-docx. Описание библиотеки доступно по ссылке. Установить библиотеку можно командой:

!pip install python-docx

Общий алгоритм разработки Документа представляет собой последовательность шагов:

  1. Подготовка шаблона документа;

  2. Подготовка скрипта сборки на Python;

  3. Обновление содержания и числа страниц;

  4. Сохранение в PDF.

Шаг 1

Конечный Документ проще сформировать на основе заранее подготовленного шаблона (например файл с именем template.docx). Форматирование и атрибуты (наименование программы, разработчик, децимальный номер, аннотация, текст основной части) шаблона Документа должны соответствовать ГОСТ 19.104-78.

Кроме требований ГОСТ шаблон Документа должен удовлетворять следующим требованиям (последовательность шагов описана для Microsoft Word 2019):

  • иметь поле автоматического подсчета числа страниц в строке «Листов …»:

Вставка→Экспресс-блоки→Поле→NumPages→OK
Вставка→Экспресс-блоки→Поле→NumPages→OK
  • иметь предопределенные стили для заголовков уровня 1, 2, 3, а также для кода:

Стили→Создать стиль→Дать имя→Выбрать созданный стиль правой кнопкой мыши→Формат→Абзац→Уровень
Стили→Создать стиль→Дать имя→Выбрать созданный стиль правой кнопкой мыши→Формат→Абзац→Уровень
  • иметь поле автоматической сборки содержания:

Ссылки→Оглавление→Настраиваемое оглавление
Ссылки→Оглавление→Настраиваемое оглавление
  • заголовки уровня 1, 2, 3 должны переноситься в содержание (Ссылки→Оглавление→Настраиваемое оглавление→Параметры→Выбрать созданные стили и присвоить им уровень);

  • шаблон документа должен содержать фразу, на место которой будет вставлен код программы, выполненную в необходимом стиле, например «<КОД ПРОГРАММЫ>».

Шаг 2

Папка с проектом содержит значительное число файлов, часть которых не являются кодом. К ним относятся графические файлы интерфейса программы, файлы фреймворков, а также файлы программ, используемых в процессе разработки и оставляющих после себя следы (GIT, SVN, Docker). Все эти файлы необходимо отфильтровать.

Функция, которая будет отфильтровывать только файлы, удовлетворяющие условию, выглядит следующим образом:

def check(string): 
  result = False
  if string[-3:] == '.js':
    result = True 
  if string[-4:] == '.vue':
    result = True
  if string[-5:] == '.json':
    result = True
  if string[-4:] == '.css':
    result = True
  return result

Чтобы получить список файлов, удовлетворяющих условиям фильтра, сначала необходимо получить список всех каталогов в директории проекта:

folder = []
for i in os.walk(folder_name):
    folder.append(i)

Из полученного списка каталогов читаем имена всех файлов во всех директориях проекта, и те, которые удовлетворяют условиям фильтра, складываем в отдельный список:

paths = []
for address, dirs, files in folder:
    for file in files:
        if check(file):
            paths.append(address+'\\'+file)

Перед обработкой полученного списка файлов необходимо подготовить функцию, которая будет считывать содержимое файла в одну строку с сохранением разметки переносов строк. Это необходимо для дальнейшего ускорения вставки кода в Word.

def read_file(filename):
    f = codecs.open(filename, "r", "utf_8_sig" )
    file = []
    for line in f:
        file.append(line)
    file = ''.join(file)
    f.close()
    return file

Используя функцию read_file, считываем содержимое отфильтрованных файлов, попутно вставляя строки-разделители каталогов и файлов. Это понадобится для автоматического формирования содержания.

total_code_file = []
for i, path in enumerate(paths):
    
    if i == 0:
        catalog = path[folder_name_len:].split('\')[1]
        total_code_file.append('Каталог '+catalog+'\n')
    if path[folder_name_len:].split('\')[1] != catalog:
        catalog = path[folder_name_len:].split('\')[1]
        total_code_file.append('Каталог '+catalog+'\n')
        
    total_code_file.append('Файл '+path[folder_name_len:]+'\n')
    total_code_file.append(read_file(path))
    total_code_file += '\n'

Для переноса полученного кода программы подключаемся к документу, ищем контрольную фразу, заменяем ее на пустую строку и начинаем вставлять полученный код. Если код содержит слово «Каталог», форматируем его в стиле заголовка 2 уровня, если содержит слово «Файл» – в стиле заголовка 3 уровня, остальной текст форматируем в стиле кода программы:

doc = Document(sample_file)
for p in doc.paragraphs:
    if '<КОД ПРОГРАММЫ>' in p.text:
        p.text = ''
        for line in total_code_file:
            if line.rstrip() > '' and line.split()[0] == 'Каталог':
                p.insert_paragraph_before(line.rstrip(), 'ЗАГ_2')
            elif line.rstrip() > '' and line.split()[0] == 'Файл':
                p.insert_paragraph_before(line.rstrip(), 'ЗАГ_3')
            else:
                p.insert_paragraph_before(line.rstrip(), 'КОД')

По завершении сохраняем документ. Программная обработка документа завершена.

doc.save(filename+'.docx')

Шаг 3

После вставки текста программы в Документ необходимо открыть его в Microsoft Word, выделить все (Ctrl+A) и обновить автозаполняемые поля (F9). Данную операцию необходимо выполнить дважды, так как поля обновляются последовательно, и после формирования содержания итоговое число страниц изменится.

Данная операция занимает время, так как Word выполняет расчет страниц, последовательно обрабатывая документ до конца.

Шаг 4

После завершения обновления автозаполняемых полей необходимо сохранить Документ в формате *.pdf средствами Word. Это необходимо, чтобы зафиксировать форматирование и исключить дальнейшую работу с файлом в Word, так как Word будет выполнять пересчет числа страниц при каждом открытии файла или его изменении. С *.pdf такой проблемы не будет.  

*.pdf имеет больший размер, но легко открывается любой подходящей программой. Ссылки в содержании после сохранения работают.

Полный код проекта доступен по ссылке.

Описанный вариант автоматизации не обрабатывает ошибки, связанные с разными кодировками файлов, и имеет варианты развития:

  • обработка ошибок, связанных с кодировкой файлов;

  • автоматическая выгрузка архива проекта;

  • программный запуск пересчета полей автозаполнения.

Комментарии (4)


  1. Knkplua
    08.10.2021 18:33

    OLE не существует с 1996 года, когда он преобразовался в ActiveX. И python-docx эти технологии не использует, а напрямую редактирует xml, из которых состоят документы .docx


    1. aririkatoku Автор
      08.10.2021 21:18

      Да, согласен. Спасибо что заметили. Python-docx напрямую правит xml. Правильнее сказать что на некоторых языках до сих пор сохраняется ActiveX, но в конкретно данном случае python-docx работает с xml.


      1. oleshii
        09.10.2021 11:27

        на некоторых языках до сих пор сохраняется ActiveX

        Немного скорректирую. ActiveX/COM/DCOM - технологии, существующие уже давно. Это Microsoft разработки. Они присутствуют в основном продуктовом ряде компании. Пользуетесь Вы MS Office - добро пожаловать в ActiveX. Это по стути почти одно и то же. COM, Component Object Model позволяет использовать унифицированный интерфейс, не зная реализации. DCOM, Distributed COM реализует использование этой модели удалённо, через proxy/заглушки/stubs. ActiveX есть поддержка первых двух для некомпилирующих языков программирования. Итак, все три вещи есть библиотеки. Доступ к ним из разных языков осуществляется по-разному, но существовать они будут всегда. Это одна из узловых технологий Microsoft. Не уверен, что в настоящее время кто-либо использует это в разработке.


  1. fiddle-de-dee
    13.10.2021 09:32

    Прямая генерация docx-файлов из кода имеет потолок. Более простым выглядит использование MD (например, через Фолиант), rst, Asciidoc, да даже Docbook или DITA. Общая цепочка сложнее, но сама генерация в разы проще. Для объединения текстовых файлов с исходниками это не так важно. Но завтра в документ необходимо будет внести дополнительные сведения. Послезавтра сделать подсветку синтаксиса и т.д. Всё можно запрограммировать, но мы же за эффективность.

    И, конечно, очень интересно при всех попытках борьбы с проприетарным ПО соседство слов ГОСТ и docx. Да даже если забыть о борьбе с проприетарным ПО. Та же операция обновления полей и получения pdf -- нужен MS Word. А он платный. И под linux не работает. Прощай непрерывная интеграция. На самом деле, прощай документация. Получается, документ мы делаем для галочки, чтобы сдать, а не чтобы им пользовались. Он устаревает, не успев появиться