Просто и быстро
Не так давно я столкнулся с необходимостью запротоколировать список изменений в нашем ПО. Заказчик прислал мне формуляр, который я должен был заполнить в соответствии с их внутренними требованиями к документации. Я открыл прилагавшийся к письму файл «
Изменения 1.xls
» и немного приуныл. Точнее, мне в голову последовательно пришли мысли об увольнении, а затем — о самоубийстве. Формуляр состоял из 14 колонок. Быстро перемножив в уме количество колонок с числом внесенных нами атомарных изменений (около пятисот), я пошел курить.Сделать такую работу руками мне не под силу. Большинство данных (номера новых версий, описания изменений и т. п.) у меня, конечно, имелись в наличии, но в разных местах и самых причудливых форматах. Но семьсот копипастов — увольте. Поэтому мне пришлось немного освоить PyUNO. На всякий случай — опишу вкратце процесс управления документом
OOo
из питоновского биндинга, вдруг кому пригодится.Нам потребуются сам
Open Office
, python
и, собственно, биндинги:$ sudo yum search pyuno ure
Запускаем
OOo
(нам нужны электронные таблицы, но метод работает для всех приложений — см. пример 2) с включенной опцией «слушать сокет»:$ oocalc "-accept=socket,host=localhost,port=8100;urp;" &
$ soffice "-accept=socket,host=localhost,port=8100;urp;" -writer -headless &
…и отправляемся писать код. Для начала — получим инстанс документа:
import uno
from os.path import abspath, isfile, splitext
def getDocument(inputFile) :
localContext = uno.getComponentContext()
resolver = localContext.ServiceManager.createInstanceWithContext( \
"com.sun.star.bridge.UnoUrlResolver", localContext)
try:
context = resolver.resolve( \
"uno:socket,host=localhost,port=%s;urp;StarOffice.ComponentContext" % 8100)
except NoConnectException:
raise Exception, "failed to connect to OpenOffice.org on port %s" % 8100
desktop = context.ServiceManager.createInstanceWithContext( \
"com.sun.star.frame.Desktop", context)
document = desktop.loadComponentFromURL( \
uno.systemPathToFileUrl(abspath(inputFile)), "_blank", 0, tuple([]))
Получение данных для заполнения итогового документа из текстовых файлов и change-логов я оставлю за рамками данной заметки. Пусть они у нас просто появляются в псевдополе
data
по мановению волшебной палочки. Итак, приступим к заполнению нашего документа (заполняем столбец №2):from com.sun.star.beans import PropertyValue
def fillDocument(inputFile, col, data) :
try:
sheet = getDocument(inputFile).getSheets().getByIndex(0)
row = 2
while True:
row = row + 1
val = sheet.getCellByPosition(col, row).getFormula()
if val != '' :
sheet.getCellByPosition(col, row).setFormula(val.replace(%VERSION%, data))
else :
break;
'''
All the rows are now filled, It's time to save our modified document
'''
props = []
prop = PropertyValue()
prop.Name = "FilterName"
prop.Value = "MS Excel 97"
props.append(prop)
document.storeToURL(uno.systemPathToFileUrl(abspath(inputFile)) + ".out.xls", tuple(props))
finally:
document.close(True)
Точно так же можно обойтись с любым другим столбцом. Можно даже в дороге сходить в Google Translate за переводом.
Еще некоторые полезные возможности
Вставка другого документа
'''
Required for Ctrl+G :-)
'''
from com.sun.star.style.BreakType import PAGE_BEFORE, PAGE_AFTER
def addAtTheEnd(inputFile) :
cursor.gotoEnd(False)
cursor.BreakType = PAGE_BEFORE
cursor.insertDocumentFromURL(uno.systemPathToFileUrl(abspath(inputFile)), ())
Поиск и замена
def findAndReplace(pattern, substTo, replaceAll, caseSensitive) :
search = document.createSearchDescriptor()
search.SearchRegularExpression = True
search.SearchString = pattern
search.SearchCaseSensitive = caseSensitive
result = document.findFirst(search)
while found:
result.String = string.replace(result.String, pattern, substTo)
if not replaceAll :
break
result = document.findNext(result.End, pattern)
Экспорт в PDF
def exportToPDF(outputFile)
props = []
prop = PropertyValue()
prop.Name = "FilterName"
prop.Value = "writer_pdf_Export"
props.append(prop)
document.storeToURL(uno.systemPathToFileUrl(abspath(outputFile)), tuple(props))
Disclaimer
Сразу хочу оговориться: код претендует на звание наколеночного говнокода, выполняемого один раз. Но в качестве «ну-ка быстро обновим файл отчета» — мне лично сэкономил уже кучу времени.
— Вот тут и вокруг можно собрать еще какие-то обрывки информации: http://wiki.services.openoffice.org/wiki/Uno/FAQ
— Шаблонизатор на питоне для OOo: appyframework.org
— То же самое, только для C++: habrahabr.ru/blogs/cpp/116228
Upd: В комментариях подсказывают другой способ: xlwt.
Upd2: Для генерации в более «новомодный» формат xlsx, есть такая полезная либа: xlsx.dowski.com.
За оба дополнения — огромная благодарность tanenn.