Просто и быстро
Не так давно я столкнулся с необходимостью запротоколировать список изменений в нашем ПО. Заказчик прислал мне формуляр, который я должен был заполнить в соответствии с их внутренними требованиями к документации. Я открыл прилагавшийся к письму файл «
Изменения 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.