Однажды мне понадобилось создать документы для отчетности, все они были однотипные, менялись лишь даты, ФИО, должности и списки работ. Я нашла 2 способа это автоматизировать:
при помощи функции «слияния» в ворде
при помощи Python
Автоматизация с помощью "слияния" в ворде
Самый простой способ - воспользоваться функцией «слияния» в ворде. Подробнее о том, как сделать слияние, можно посмотреть в инструкции. Здесь я сделаю краткий обзор и напишу, почему мне оно не подошло.
Для слияния нужно:
создать табличку в Excel
заполнить ее данными (ФИО, должностями и пр.):
Табличка с переменными данными открыть вордовский файл с шаблоном отчета:
Шаблон вставить в места, где данные разнятся, названия соответствующих полей из эксельки
Шаблон со вставленными полями из Excel нажать чудо-кнопку
генерация отчетов
и дальше чудо-функция генерила столько отчетов, сколько строк в эксельке.
Есть одно но: все отчеты сохраняются в виде одной большой pdf-ки, что совершенно не годилось: мне они нужны были в формате ворд, каждый отдельным файлом.
Один из способов получить отчеты в нужном формате - создать столько копий файла-шаблона, сколько нужно отчетов:

затем каждый поочередно открыть, включить режим предпросмотра:

прокрутить до нужного "образца" (номера строки Excel, из которой были взяты данные):

затем сохранить в таком виде и закрыть. И так для каждого отчета по порядку. Хлопотно, но возможно.
Примечание. Как видите, список работ вставляется корректно, заданное в шаблоне форматирование сохраняется. Чтобы каждый пункт списка был с новой строки, достаточно в Excel написать его с новой строки:

Также можно было конвертировать pdf-ку в ворд и разбить на файлы, но я не нашла конвертер, после которого бы не слетали шрифты и форматирование, что для меня было критично.
Автоматизация с помощью Python
Я стала искать другой способ, и мне помогла статья на Хабре про автоматизацию с помощью Python. Я очень рекомендую с ней ознакомиться для полноты картины. В ней был приведен скрипт, который делает то же самое, что и “слияние”, но каждый отчет кладет в отдельный ворд. Я немного доработала его под свои нужды:
Так как в эксельке я использовала формулы для заполнения таблички, мне нужно было чтоб скрипт брал из ячеек не сами формулы, а результат их выполнения. Для этого я добавила параметр
data_only=True
в функцию которая считывает эксельку:wb = openpyxl.load_workbook(filename='данные для слияния.xlsx', data_only=True)
Если не вставить этот параметр, то вот какой отчет вы можете получить:
Исходная таблица. Даты вычисляются с помощью формул Формулы вместо значений Чтобы корректно вставить список, воспользовалась классом
Listing
:listTO = Listing(sheet['D'+str(num)].value.replace('\n', '\a'))
Он преобразует строку в список, в качестве разделителя пунктов списка использует "\a". Поэтому я заменила перенос строки в исходном тексте "\n" на "\a". Если не сделать это преобразование, список вставится одним куском:
Список, вставленный без использования Listing Вы могли заметить, что вместе с датой выводится еще и время:
Вывод даты Чтобы оставить только дату, можно воспользоваться классом
datetime
:date =
datetime.date
(sheet['A'+str(num)].value)
Примечание. Если заполнить таблицу Excel, а потом удалить часть строк снизу, Python всё равно “увидит” их как пустые ячейки. Поэтому при генерации отчётов скрипт создаст лишние документы с пропусками вместо данных, либо выдаст ошибку такого вида:

Чтобы это исправить, можно:
Создать новый лист в Excel
Скопировать туда все данные со старого листа
Использовать новый лист для работы скрипта
Это решит проблему и скрипт будет обрабатывать только заполненные строки с данными.
Итоговый код:
import openpyxl
from docxtpl import DocxTemplate
from docxtpl import Listing
from datetime import datetime
wb = openpyxl.load_workbook(filename='данные для слияния.xlsx', data_only=True)
sheet = wb['Лист1']
doc = DocxTemplate('шаблон.docx')
for num in range(2,len(list(sheet.rows))+1):
date = datetime.date(sheet['A'+str(num)].value)
FIO = sheet['B'+str(num)].value
position = sheet['C'+str(num)].value
listTO = Listing(sheet['D'+str(num)].value.replace('\n', '\a'))
context = {
'date': date,
'FIO': FIO,
'position': position,
'listTO': listTO,
}
doc.render(context)
doc.save(f"Отчет о ТО {num}.docx")