Порой возникают задачи, когда возникает необходимость формировать отчеты и прочие документы. В моей практике данная задача возникала не раз.
Проекты, в которых возникала данная задача:
- складской учет
- учет объектов недвижимости
- документооборот
Первый мой опыт был с MS Office, но позже возникла необходимость в разработке кросс-платформенных решений, посему выбор пал на OpenOffice. Т.к. в большинстве случаев нужно было формировать таблицы, то был выбран OpenOffice Calc.
Сперва я решал подобные задачи на С++, разработал библиотеку, которую использовал в разных проектах. Все было хорошо, но чуть ли не каждая минорная версия офиса требовала пересборки библиотеки, а не редко и правки кода. Под каждый дистрибутив и его новую версию приходилось в лучшем случае пересобирать пакеты, править пути с учетом версии офиса, замену OpenOffice на LibreOffice и т.п. А это очень утомительно и отнимает время.
Все эти трудности привели к поиску более простых решений. Я посмотрел список поддерживаемых языков и увидел Python, что меня очень заинтересовало, т.к. уже довольно активно его начал использовать. Нашел простенький пример, попробовал — работает. Проверил на разных версиях и — о, чудо! Без малейших правок один и тот же скрипт работает на разных дистрибутивах и версиях офиса.
Те, кто уже сталкивался с подобными задачами — знает, что ковыряние API дело не благодарное. Посему первым делом я принялся искать уже готовые библиотеки. Наиболее интересным вариантом для меня оказался PyOO. Данная библиотека содержит довольно обширный функционал и проста в использовании. В ней реализованы функции от создания/сохранения документов до объединения ячеек и создания диаграмм. Но… Ни одна из найденных мной библиотек не обеспечивала необходимый мне функционал. Много вкусного, но лишнего для меня.
Вернемся к изначальной задаче: необходимо формировать отчеты и счета. Как уже упоминалось выше, шаблоны проще всего создавать в офисном пакете. Ведь их смогут создавать и конечные пользователи без участия разработчиков.
С созданием шаблонов все ясно, но как знать, куда необходимо вставлять данные. Можно использовать индексы (колонка, строка) или имя (например: «E5»).
И что если для одного отчета необходимо создать несколько шаблонов для генерации документа. Например один шаблон для книжного расположения, второй для альбомного. Но нет никакой гарантии, что и в одном и в другом шаблоне одно и то же поле (данные) необходимо вставить по одному и тому же имени (или индексу) ячейки. А это означает, что нужно хранить данные о расположении полей в шаблоне. Но есть более простое универсальное решение. Данную задачу можно реализовать через NamedRange.
NamedRange — это имя ячейки или области на листе. При этом NamedRange уникальный в рамкой одной книги (документа).
Еще одна необходимая функция для решения данной задачи — вставка строк. При этом вставка должна быть с учетом сохранения форматирования строки (шрифт, объединенные ячейки и т.п.).
Исходя из вышеперечисленных задач, был разработан модуль на Python, который реализует следующие функции:
- Открытие и создание документов
- Сохранение документов разных форматах (поддерживаемых Libre/Open Office)
- Вставка/удаление листов
- Вставка строк с копированием форматирования
- Вставка/получения значения имен (NamedRange)
- Вставка/получения значения ячеек по имени (индексу)
Пример использования:
import pyloo
# open document
doc = pyloo.Document()
file_name = os.getcwd() + "/example.ods"
doc.open_document(file_name)
# Get document fields
fields = doc.fields()
# Get field "HEADER"
field = fields.field("HEADER")
print ("Document header is: " + str(field.is_null()))
# Set values
field = fields.field("TABLE_NAME")
field.set_value("Test table name")
print ("New table name is: " + field.value())
# Insert 5 rows
field1 = fields.field("FIELD_1")
num_rows = 5
step = 2
if num_rows > 0:
field1.insert_rows(num_rows=num_rows-1, step=step, columns_to_copy=200)
for i in range(1, num_rows + 1):
field1.set_value("F1." + str(i), 0, i * step - (step - 1))
# Set value="value1" at column=1, row=1 (B1)
sheet = doc.sheets().sheet(0)
sheet.set_cell_value_by_index(1, 0, "value1")
print (sheet.cell_value_by_index(1, 0))
Уверен, что данная задача возникала не только у меня. Рад, если кому-то пригодится.
Пример документа с данными требованиями и пример использования библиотеки находятся в каталоге examples.
Скачать: PyLOO
Автор: thepurple