В этой статье мы хотели бы поделиться кейсом о том, как собрать документацию по проектам заказчика с помощью Сonfluence.

Скорее всего вы знаете, что такое Confluence и для чего он нужен. Если нет, коротко скажем, что это пространство/сайт, где вы копите все знания о вашей деятельности в организации. То есть, например, выполняя какой-либо проект, параллельно ведете свой раздел в Confluence, чтобы новый сотрудник смог быстрее в нем разобраться. Также это мощный инструмент для различной аналитики, ведения статистики, но, если вам потребуются дополнительные инструменты и «фишки», нужно будет их оплатить, так как они не будут доступны в бесплатной версии.

Специалист Neoflex из подразделения Big Data Solutions рассказывает о проблеме, с которой он столкнулся:

При введении своего раздела в Confluence стараешься сразу же описывать документацию для клиента (руководство администратора), а вот забрать/экспортировать страницу в Word получается только по одной странице, и приходилось объединять все это руками в один документ. Поэтому я приступил к реализации своего микросервиса по сбору документов и созданию документации.

Зная такие инструменты как Selenium и язык программирования Python, мною была написана рекурсивная функция от нужного отдела по всем его дочерним объектам. В ходе выполнения наткнулся на большое количество проблем: например, отсутствие id в url, принадлежность одной страницы другому разделу, медленная работа, несоответствие стилей и т.д. Вся работа строилась на простом алгоритме: проходить все страницы, сохранять необходимый текст в тегах в html файл для дальнейшего преобразования в DOCX. Почему пришлось отказаться от данного подхода:

  1. Время работы. Selenium необходимо открыть страницу и посмотреть определенные теги.  Обычно используют sleep() для того, чтобы страница прогрузилась;

  2. Сложность самого Confluence. Он не показывает вложенные объекты в подразделе пока не будет выбран данный раздел (ниже приведен пример);

  3. Разметка таблиц, заголовки, артефакты по тексту (в конце страницы, где была таблица, вставляется строка с функциями над таблицами в Сonfluence);

  4. И самое главное – это отсутствие картинок.

После этого я вернулся к поиску уже готовых решений в интернете и нашел Confluence API. Это и положило новое начало в разработке инструмента. Все, что мне нужно было сделать для работы – это скачать библиотеку, подключиться через логин и пароль, указать начальное пространство для работы скрипта.

Нам понадобится установить библиотеку atlassian-python-api и уже можно работать с данным API.

Документация по API довольно большая, но мне пригодится лишь пара команд (для ознакомления можно почитать данный ресурс).

Для подключения достаточно написать код:

from atlassian import Confluence
url='https://confluence.ru/'#ссылка на начальную страницу.
username='твой_логин'
password='твой_пароль'

confluence = Confluence(
    url=url,
    username=username,
    password=password)

Далее необходимо получить дочерние страницы и скачать файлы.

Весь код с комментариями представлен ниже:

from atlassian import Confluence
def get_id(url_full):
    return url_full.split('=')[-1]

def get_child(id):
    t = confluence.cql(cql='parent={0}'.format(id), start=0, limit=None, expand=None,include_archived_spaces=None, excerpt=None)['results']
    return sort_json(t)
 
def cursor(list_child,level=0):
    for i in list_child:
        id = i['content']['id']
        #проверка на наличие дочерних элементов
        if len(get_child(id))>0:
            page = confluence.get_page_by_id(page_id=id)
            response = confluence.get_page_as_word(page['id'])
            contents.append(response)
            #проходим снова в рекурсию
            cursor(get_child(id),level=level+1)
        else:
            page = confluence.get_page_by_id(page_id=id)
            response = confluence.get_page_as_word(page['id'])
            contents.append(response)
 
def start_parser(url,url_full):
    #Получили ID родителя
    id_url = get_id(url_full)
    #получаем содержимое страницы
    page = confluence.get_page_by_id(page_id=id_url)
    #сохраняем вордовский контент страницы(экспорт страницы)
    response = confluence.get_page_as_word(page['id'])
    contents.append(response)
    #Получаем вложенные объекты
    list_child = get_child(id_url)
    #начинаем рекунсию
    cursor(list_child)
    return page['title']
 
def save_files(contents):
    for i,j in enumerate(contents):
        with open(f'{i}.doc', mode='wb') as file_pdf:
            file_pdf.write(j)
 
def sort_json(json_list):
    titles = list()
    for i in json_list:
        titles.append(i['title'])
    titles.sort()
    sort_json = list()
    for i in titles:
        for j in json_list:
            if i==j['title']:
                sort_json.append(j)
    return sort_json
 
url='https://confluence.ru/'
url_full='https://confluence/pages/viewpage.action?pageId=*******'
 
confluence = Confluence(
    url=url,
    username='твой_логин',
    password='твой_пароль')
 
contents = list()
#начало работы по сбору страниц
global_title = start_parser(url,url_full)
#загрузка страниц в doc файлы
save_files(contents)

На текущий момент у нас собираются все дочерние элементы и скачиваются в корневую папку файлы DOC, которые нужно преобразовать в DOCX. С этим мне помогла стандартная библиотека win32com.

На вход подается список путей к файлам DOC:

def convert_doc_to_docx(files):
    for path in files:
        word = win32.gencache.EnsureDispatch('Word.Application')
        doc = word.Documents.Open(path)
        doc.Activate()
        # переименовываем файлы в docx.
        new_file_abs = os.path.abspath(path)
        new_file_abs = re.sub(r'\.\w+$', '.docx', new_file_abs)
        # сохраняем и закрываем
        word.ActiveDocument.SaveAs(
            new_file_abs, FileFormat=constants.wdFormatXMLDocument
        )
        doc.Close(False)
        os.remove(path)

Конвертировать файлы требуется для дальнейшей работы библиотекой python-docx, которая будет склеивать все документы между собой, а также для сохранения всех стилей.

На вход подается список имен файлов

def composer(files):
    master = Document('qwe.docx')
    composer = Composer(master)
    for file in files:
        doc2 = Document(file)
        if file != files[-1]:
            doc2.add_page_break()
        composer.append(doc2)
    composer.save(f"{global_title}.docx")

Разберем подробнее:

  • master = Document('qwe.docx') – это подготовленный ранее мною шаблон со стилями, и шаблон визуального оформления страницы. Сюда мы и будем «дергать» по очереди каждый файл и добавлять разделение страниц;

  • composer = Composer(master) – выбираем master файл как целевой и будем именно в него добавлять другие файлы docx;

  • doc2.add_page_break() – данная функция позволяет вставить «разрыв страницы», чтобы отделить разделы между собой;

  • composer.append(doc2) – добавляем информацию с doc2 в целевой раздел;

  • composer.save (f"{global_title}.docx") – по завершению сохраняем файл с наименованием, которое получили в самом начале алгоритма сбора документации.

В итоге мы получили файл DOCX со всеми вложенными страницами по разделу проекта с Confluence. Сохранили все картинки, таблицы и стили, а также добавили свой шаблон для визуального оформления страниц в Word.

Подводя итоги, можно считать, что данный инструмент получился полезным. Мы использовали API для получения дочерних элементов и скачивания страницы из раздела, а также библиотеку python-docx для сбора всех файлов в один – целевой и ранее созданный шаблон под клиентов.

Весь код выложен в github 

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