【Python】【Xmind】解析工具 xmind用例轉為excel用例(不限級數 多少級都可以)


 

一、圖標說明

 

 

二、效果

xmind備注

【前置條件】
【操作步驟】
【SQL校驗】
【預期結果】
【備注】

 

三、解析XMind

備注:已封裝好 可直接調用

JarExcelUtil工具類:https://www.cnblogs.com/danhuai/p/13538291.html

Xmind版本:XMind 8

"""
-*- coding:utf-8 -*-
@Time   :2020/9/4 15:07
@Author :維斯
@File   :jar_xmind_util.py
@Version:1.0
"""
import json

import xmind

from common.jar_excel_util import JarExcelUtil


class JarXmindUtil:
    def __init__(self):
        # markers字段 "markers": ["star-orange"]
        self.model = ['star-dark-gray', '$所屬模塊$']  # [所屬模塊(灰色星星標志),標識]
        self.check = ['star-orange', '$驗證點$']  # [驗證點(黃色星星標志),標識]
        self.except_case = ['people-red', '$異常$']  # [異常用例(紅色人像標志),標識]
        self.normal_case = ['people-green', '$正常$']  # [正常用例(綠色人像標志),標識]
        self.priority_1 = ['priority-1', '$高$']  # [重要級別-高(1號優先級標志),標識]
        self.priority_2 = ['priority-2', '$中$']  # [重要級別-中(2號優先級標志),標識]
        self.priority_3 = ['priority-3', '$低$']  # [重要級別-低(3號優先級標志),標識]

        # 匯總
        self.all_markers = [self.model,
                            self.check,
                            self.except_case,
                            self.normal_case,
                            self.priority_1,
                            self.priority_2,
                            self.priority_3
                            ]

        self.note = ['note', '$備注節點$']
        self.comment = ['comment', '$批注節點$']

        # 節點分割符
        self.node_split = ' /'  # 節點分割符
        self.node_split_excel = ' /'  # 寫入表格中的多節點分割符
        # 預期結果分割符
        self.node_expect_split = '預期結果:'

        # 用例編號前綴
        self.case_number_model = 'CASE_200917'  # 生成的用例編號 如 CASE_200917000001、CASE_200917000002

        # Xmind備注信息模板解析(嚴格按照此順序解析)
        self.xmind_note_model = [['【前置條件】', '內容'],
                                 ['【操作步驟】', '內容'],
                                 ['【SQL校驗】', '內容'],
                                 ['【預期結果】', '內容'],
                                 ['【備注】', '內容']]

        # 測試用例表格
        self.headers = [
            ['用例編號', len(self.case_number_model) + 5 + 5],  # 長度多5個字符
            ['用例類型'],
            ['重要級別'],
            ['所屬模塊', 20],
            ['驗證點', 20],
            ['用例標題', 60],
            ['前置條件', 12],
            ['操作步驟', 12],
            ['SQL校驗', 12],
            ['預期結果', 12],
            ['備注', 12]
        ]

    @staticmethod
    def del_endswith_none(str1: str):
        """
        刪除字符串首尾的空字符(空格、換行)
        :param str1:
        """
        s = str1
        if str1 is not None:
            while True:
                if s.endswith(' ') or s.endswith('    ') or s.endswith('\r\n') or s.endswith('\r') or s.endswith('\n'):
                    s = s[:-1]
                elif s.startswith(' ') or s.startswith('    ') or s.startswith('\r\n') or s.startswith(
                        '\r') or s.startswith('\n'):
                    s = s[1:]
                else:
                    break
        return s

    def to_excel(self, out_file, result_node: list):
        """
        解析的Xmind數據寫入表格文件
        :param out_file: 表格文件
        :param result_node: 列表 每一個元素為一個完整的xmind路徑
        """
        body_data = []
        count = 0
        for re in result_node:
            count += 1
            # 1 分割某鏈路所有節點
            node_list = re.split(self.node_split)
            # 2 判斷節點屬性
            re_model = ''  # 所屬模塊
            re_check = ''  # 驗證點
            re_case_type = ''  # 用例類型
            re_case_name = ''  # 用例標題
            re_priority = ''  # 重要級別
            re_expect = ''  # 預期結果
            re_note = ''  # 備注(xmind中的備注)
            re_comment = ''  # 批注(xmind中的批注)

            for n in node_list:
                n: str
                # 2.1 所屬模塊
                if n.startswith(self.model[1]):
                    re_model += n.replace(self.model[1], '') + self.node_split_excel
                # 2.3 驗證點
                if n.startswith(self.check[1]):
                    re_check += n.replace(self.check[1], '') + self.node_split_excel
                # 2.4 用例類型&用例標題
                if n.startswith(self.except_case[1]) or n.startswith(self.normal_case[1]):
                    if n.startswith(self.except_case[1]):
                        re_case_type = self.except_case[1][1:-1]
                        re_case_type_swap1 = n.split(self.except_case[1])
                        if re_case_type_swap1 == 3:
                            re_case_name += re_case_type_swap1[1] + self.node_split_excel
                        else:
                            re_case_name += n.replace(self.except_case[1], '') + self.node_split_excel
                    if n.startswith(self.normal_case[1]):
                        re_case_type = self.normal_case[1][1:-1]
                        re_case_type_swap2 = n.split(self.normal_case[1])
                        if re_case_type_swap2 == 3:
                            re_case_name += re_case_type_swap2[1] + self.node_split_excel
                        else:
                            re_case_name += n.replace(self.normal_case[1], '') + self.node_split_excel
                # 2.5 重要級別
                if n.startswith(self.priority_1[1]):
                    re_priority = self.priority_1[1][1:-1]
                if n.startswith(self.priority_2[1]):
                    re_priority = self.priority_2[1][1:-1]
                if n.startswith(self.priority_3[1]):
                    re_priority = self.priority_3[1][1:-1]
                # 2.6 預期結果
                re_expect = n[n.find(self.node_expect_split):]
                re_expect = re_expect[len(self.node_expect_split):]
                re_expect = self.del_endswith_none(re_expect)
                # 2.7 備注
                re_note_swap = n.split(self.note[1])
                if len(re_note_swap) == 3:
                    re_note = re_note_swap[1]
                # 2.8 批注
                re_comment_swap = n.split(self.comment[1])
                if len(re_comment_swap) == 3:
                    re_comment = re_comment_swap[1]

            # 刪除末尾的分割符
            length = len(self.node_split_excel)
            re_model = re_model[:len(re_model) - length]
            re_check = re_check[:len(re_check) - length]
            re_case_name = re_case_name[:len(re_case_name) - length]
            # 刪除用例名稱后的 預期結果、空格、換行
            index = re_case_name.find(self.node_expect_split)
            re_case_name = re_case_name[:None if index == -1 else index]
            re_case_name = self.del_endswith_none(re_case_name)

            # 解析XMind中的備注信息
            for n in range(len(self.xmind_note_model)):
                # 提取名稱(如:【前置條件】 提取為 前置條件) 分別去除前后1個字符
                n_name = self.xmind_note_model[n][1:-1]
                # 提取此名稱下的數據(如:提取哪些內容是前置條件)  截取XMind中備注信息中 n至n+1中的數據
                if n < len(self.xmind_note_model) - 1:
                    n_data = re_note[
                             re_note.find(self.xmind_note_model[n][0]):re_note.find(self.xmind_note_model[n + 1][0])]
                else:
                    n_data = re_note[re_note.find(self.xmind_note_model[n][0]):]
                n_data = n_data[len(self.xmind_note_model[n][0]):]
                n_data = self.del_endswith_none(n_data)
                self.xmind_note_model[n][1] = n_data

            # 3 拼接節點至指定表格字段
            body = [
                self.case_number_model + str(count).zfill(6),  # 用例編號
                re_case_type,  # 用例類型
                re_priority,  # 重要級別
                re_model,  # 所屬模塊
                re_check,  # 驗證點
                re_case_name,  # 用例標題
                self.xmind_note_model[0][1],  # 前置條件
                self.xmind_note_model[1][1],  # 操作步驟
                self.xmind_note_model[2][1],  # SQL校驗
                self.xmind_note_model[3][1],  # 預期結果
                self.xmind_note_model[4][1]  # 備注
            ]
            body_data.append(body)
        JarExcelUtil(header_list=self.headers).write(out_file=out_file, data_body=body_data)
        print('數據寫入Excel完成!(路徑:{})'.format(out_file))

    @staticmethod
    def analysis(xmind_path):
        """
        解析Xmind文件(獲取每條完整路徑)
        :param xmind_path: xmind文件
        :return: 返回所有路徑list
        """
        wb = xmind.load(xmind_path)
        data = wb.to_prettify_json()
        data = json.loads(data)
        result_data = []
        # 畫布
        for data_topic in data:
            # 1級
            data_1 = data_topic.get('topic')
            title_1 = data_1.get('title')

            # 遞歸后面所有級(2級、3級、......)
            JarXmindUtil().__base(title_1, data_1, result_data)
        print('{},xmind數據解析完成!'.format(xmind_path))
        # print(*result_data, sep='\n')
        return result_data

    def __base(self, title_long_x, data_topics_x, result_data_all):
        """
        遞歸Xmind所有子標題
        :param title_long_x:
        :param data_topics_x:
        :param result_data_all:
        """
        # 遞歸所有級
        topics_list = data_topics_x.get('topics')
        for topics in topics_list:  # 循環所有路徑
            title = ''
            swap = ''
            # 取節點圖標屬性
            markers: list = topics.get('markers')
            if len(markers) != 0:
                for marker in markers:  # 循環該節點所有屬性
                    for all_mar in self.all_markers:  # 循環預定所有屬性值
                        if marker == all_mar[0]:  # 節點markers屬性中有預定值

                            # 若是用例名稱節點 則獲取備注與批注信息
                            if marker == self.all_markers[2][0] or marker == self.all_markers[3][0]:  # 綠色人像或紅色人像
                                # 是用例節點
                                s_note = topics.get('note') if topics.get('note') is not None else ''
                                s_comment = topics.get('comment') if topics.get('comment') is not None else ''
                                title = '{}{}{}{}{}'.format(title_long_x + swap, self.node_split,
                                                            all_mar[1] + topics.get('title') +
                                                            all_mar[1] + self.node_split,
                                                            self.note[1] + str(s_note) +
                                                            self.note[1] + self.node_split,  # 備注
                                                            self.comment[1] + str(s_comment) + self.comment[
                                                                1] + self.node_split)  # 批注
                                swap = self.node_split + all_mar[1] + topics.get('title') + all_mar[
                                    1] + self.node_split + self.note[
                                           1] + str(s_note) + self.note[1] + self.node_split + self.comment[
                                           1] + str(s_comment) + self.comment[1]
                            else:
                                # 非用例節點
                                title = '{}{}{}'.format(title_long_x + swap, self.node_split,
                                                        all_mar[1] + topics.get('title') + all_mar[1] + self.node_split)
                                swap = self.node_split + all_mar[1] + topics.get('title') + all_mar[1]
                            break


                        else:
                            # 循環到最后 沒匹配到(說明此標志沒有在預定標志中)
                            if markers[len(markers) - 1] == marker \
                                    and self.all_markers[len(self.all_markers) - 1] == all_mar:
                                print('節點:【{}】,在預定標志中未匹配到此標志({})'.format(topics.get('title'), marker))
            else:
                title = '{}{}{}'.format(title_long_x, self.node_split, topics.get('title'))

            # 取節點值
            if topics.get('topics') is not None:
                JarXmindUtil().__base(title, topics, result_data_all)
            else:
                result_data_all.append(title)
                continue


if __name__ == '__main__':
    ju = JarXmindUtil()
    # XMind文件
    path = 'E:\\xxx項目測試用例.xmind'

    # 用例編號前綴
    ju.case_number_model = 'CASE_201101'

    # step1 解析xmind
    result = ju.analysis(path)
    # step2 寫入excel
    ju.to_excel('xxx項目測試用例.xlsx', result)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM