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

Проекты, в которых возникала данная задача:

  • складской учет
  • учет объектов недвижимости
  • документооборот

Первый мой опыт был с 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

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


  1. impwx
    15.12.2015 12:58
    +1

  1. thepurple
    15.12.2015 13:17
    +1

    Да, я как-то сразу и не обратил внимание…
    Забыл за это значение, подумаю.