首页 > 其他分享 >xmind&百度脑图之导入导出

xmind&百度脑图之导入导出

时间:2024-09-23 23:16:30浏览次数:9  
标签:topic1 脑图 topic 导入 xmind workbook path data

背景

应公司业务要求,开发一款支持多人在线编辑思维导图的产品,且要做到思维导图文件导入导出。前端组件 vue-testcase-minder-editor

组件作者:https://github.com/chenhengjie123/vue-testcase-minder-editor?tab=readme-ov-file

数据格式对比

#百度脑图数据格式
{'data': {'id': 'b7697411-883d-4494-933d-7da80c3fc63f', 'created': 1722849031301, 'text': '导出'
    }, 'children': [
        {'data': {'id': '2ecec96e-7364-4ea9-a2c1-dec85180a919', 'created': 1722849031301, 'text': '导出主题 1'
            }, 'children': [
                {'data': {'id': 'fb9a7734-19da-475f-9c9d-4ce152db3df8', 'created': 1722849031301, 'text': '哈哈哈哈哈'
                    }, 'children': []
                },
                {'data': {'id': '7b0ffb3e-fafd-42d2-b62e-20ff3e657ab0', 'created': 1722849031301, 'text': '高大上的v'
                    }, 'children': []
                }
            ]
        },
        {'data': {'id': 'cbf24130-3b2d-4a7a-bbd1-28a85cc0f514', 'created': 1722849031301, 'text': '导出主题 2'
            }, 'children': []
        }
    ]
}


# xmind数据格式
[
    {'title': '画布 1', 'topic': {'title': '导出', 'topics': [
                {'title': '导出主题 1', 'topics': [
                        {'title': '哈哈哈哈哈', 'id': '6c4bbf9b-981c-44c7-9aa6-16d0c65a40f9'
                        },
                        {'title': '高大上的v', 'id': 'ea0e2537-9d51-4fdc-99df-13e44976a881'
                        }
                    ], 'id': 'f7391556c0c84b1d9c259b582e'
                },
                {'title': '导出主题 2', 'id': '42b829374fcbb84ccd7af93f02'
                }
            ], 'id': '17da8339e188988536c24ba1ce'
        }, 'structure': 'org.xmind.ui.map.clockwise', 'id': 'bada207e86cb919b28692b4656'
    }
]

导入--xmind转百度脑图数据格式

# 处理xmind导入数据格式
def transform_data(input_data):
    def transform_node(node):
        transformed_node = {
            'data': {
                'id': generate_unique_id(),  # You need to implement generate_unique_id()
                'created': generate_timestamp(),  # You need to implement generate_timestamp()
                'text': node['text']
            },
            'children': [transform_node(child) for child in node.get('children', [])]
        }
        return transformed_node
    # Helper functions to generate unique IDs and timestamps (epoch time in milliseconds)
    def generate_unique_id():
        # You can use UUID or any other method to generate unique IDs
        # Example implementation using UUID:
        import uuid
        return str(uuid.uuid4())
    import time
    def generate_timestamp():
        # Generate current time in milliseconds
        return int(round(time.time() * 1000))

    # Start transformation from the root node
    transformed_data = transform_node(input_data['data'])
    return transformed_data
  
  ## 导入接口
   def importParticulars(self, request, **kwargs):
        # 获取当前脚本的文件路径
        script_path = os.path.abspath(__file__)
        # 获取当前脚本的目录(不包含文件名)
        script_dir = os.path.dirname(script_path)
        # 构建当前目录下的file文件路径
        file_path = os.path.join(script_dir, 'file/')
        # print(file_path)
        # 清空file文件夹
        folder_to_clear = file_path
        clear_folder(folder_to_clear)
        fs = FileSystemStorage(location=file_path)
        particulars_id = request.data["particulars_id"]
        file_data = request.data["file_data"]
        # 接收前端传入数据存到file文件夹中 .xmind格式
        uploaded_file_name = fs.save(file_data.name, file_data)
        xmindparser.config = {
            'showTopicId': True,  # 是否展示主题ID
            'hideEmptyValue': True  # 是否隐藏空值
        }
        # 文件存的路径
        filePath = os.path.join(file_path, uploaded_file_name)
        # .xmind格式转成 .json格式文件
        content = xmindparser.xmind_to_json(filePath)
        # 读取json文件
        with open(content, 'r', encoding='utf-8') as f:
            data = json.load(f)
        #  判断是否空文件
        if len(data) == 0:
            return APIRespones('1001', '空文件不可导入~', False)
        res = data[0]['topic']
        # 将res(字典格式)转成 JSON 字符串
        original_json = '''{}'''.format(res)
        # 使用正则表达式进行替换
        converted_json = re.sub(r"'title':", "'created':'','text':", original_json)
        # 将单引号转换为双引号,以符合 JSON 格式要求
        converted_json = converted_json.replace("'", '"')
        converted_json = re.sub(r'"([^"]+)"[\s\S]"([^"]+)"', r'\1\2', converted_json)
        # print("converted_json",converted_json)
        # converted_json = process_data(converted_json)

        # 将转换后的 JSON 字符串加载为 Python 对象(字典)
        converted_data = json.loads(converted_json)
        # 再转一次(topics --> children)
        original_json2 = '''{}'''.format(converted_data)
        converted_json = re.sub(r"'topics':", "'children':", original_json2)
        converted_json = converted_json.replace("'", '"')
        converted_data2 = json.loads(converted_json)
        finalData = {}
        finalData['data'] = converted_data2
        # 调用封装好的transform_data方法转数据格式,得到最终的前端组件可以渲染的数据格式
        transformed_data = transform_data(finalData)
        xmind_particulars.objects.filter(pk=particulars_id).update(file_data=transformed_data)
        data = xmind_particulars.objects.filter(pk=particulars_id).values()
        return APIRespones('1000', '文件导入成功~', True, data)

导入--百度脑图转xmind数据格式,并下载文件到本地

# 导出相关方法封装
"""
# 通过下方递归方法来改造以上这段代码
设置xmind文件主题以及子主题
        for subtopic in map['children']:
            print(9999,subtopic)
            topic1 = root_topic1.addSubTopic()
            topic1.setTitle(subtopic['data']['text'])
            for subtopic2 in subtopic['children']:
                print(888,subtopic2)
                topic2 = topic1.addSubTopic()
                topic2.setTitle(subtopic2['data']['text'])
                for subtopic3 in subtopic2['children']:
                    print(777, subtopic3)
                    topic3 = topic2.addSubTopic()
                    topic3.setTitle(subtopic3['data']['text'])
                    for subtopic4 in subtopic3['children']:
                        print(777, subtopic4)
                        topic4 = topic3.addSubTopic()
                        topic4.setTitle(subtopic4['data']['text'])
"""
# 通过递归来改造以上这段代码
def add_subtopics(parent_topic, subtopics):
    for subtopic in subtopics:
        new_topic = parent_topic.addSubTopic()
        new_topic.setTitle(subtopic['data']['text'])
        # 如果有子主题,递归调用
        if 'children' in subtopic:
            add_subtopics(new_topic, subtopic['children'])




"""
踩坑:xmind python代码save后用xmind2020报错无法打开:原因xmind8格式无法用xmind2020打开,META-INF文件夹缺失,使用以下方法可解决 
***************************************
xmind改成zip文件,
解压后 在目录内创建 META-INF文件夹
META-INF文件夹内创建 manifest.xml文件

文件内容:
'<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>'

重新打包后zip文件 然后后缀改成 xmind
"""
def repair(fname):
    zip_file = zipfile.ZipFile(fname, 'a')
    zip_file.writestr('META-INF/manifest.xml', '<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>')
    zip_file.close()

    
# 导出xmind接口
def exportMindMap(self, request, **kwargs):
  # 前端上传的文件数据
  map = request.data["map"]
  # 获取当前脚本的文件路径
  script_path = os.path.abspath(__file__)
  # 获取当前脚本的目录(不包含文件名)
  script_dir = os.path.dirname(script_path)
  # exportFile文件夹路径
  exportFile_path = os.path.join(script_dir, 'exportFile')
  # files = glob.glob(os.path.join(exportFile_path, '*.xmind'))
  # # 删除后缀为 .xmind 的文件
  # for file in files:
  #     os.remove(file)
  clear_folder(exportFile_path) # 先清空exportFile文件

  # 定义xmind文件名称
  filename = 'output.xmind'
  # 构建exportFile目录下的 'output.xmind' 文件路径
  file_path = os.path.join(exportFile_path, filename)
  # 创建一个新的xmind文件或加载已有xmind文件
  workbook = xmind.load(file_path)
  # 获取第一个画布
  first_sheet = workbook.getPrimarySheet()
  # 设置画布名称
  first_sheet.setTitle(map['data']['text'])
  # 获取画布中心主题,默认创建画布时会新建一个空白中心主题
  root_topic1 = first_sheet.getRootTopic()
  # 设置主题名称
  root_topic1.setTitle(map['data']['text'])
  # map['children'] 是最顶层的子主题
  add_subtopics(root_topic1, map['children'])
  # 保存并覆盖原始文件
  xmind.save(workbook)
  # 日志
  develop.log.info('这是一个info级别的日志'+ file_path)
  # 此方法解决生成的xmind文件本地使用xmind8之后的版本无法打开的问题
  repair(file_path)
  # 检查文件是否存在
  if not os.path.exists(file_path):
    return HttpResponse(status=404)
  # 导出文件命名
  xmindFileName = map['data']['text']
  # 返回文件响应
  response = FileResponse(open(file_path, 'rb'), content_type='application/xmind')
  response['Content-Disposition'] = f'attachment; filename="{xmindFileName}"'
  return response

python操作xmind文件

XMind-1.2.0.tar.gz

下载地址:
https://files.pythonhosted.org/packages/7c/8c/e13a82fa9b0394c0d58248196d7d51d7274407cdebc1df36b76034ab990d/XMind-1.2.0.tar.gz

创建及更新xmind文件

#!/usr/bin/env python

# -*- coding:utf-8 -*-

 

import xmind

from xmind.core.const import TOPIC_DETACHED

from xmind.core.markerref import MarkerId

from xmind.core.topic import TopicElement

 

# 加载已有xmind文件,如果不存在,则新建

workbook = xmind.load('D:\\example\\example.xmind')

 

first_sheet = workbook.getPrimarySheet()  # 获取第一个画布

first_sheet.setTitle('First Sheet')   # 设置画布名称

root_topic1 = first_sheet.getRootTopic()  # 获取画布中心主题,默认创建画布时会新建一个空白中心主题

root_topic1.setTitle('Example Topic')   # 设置主题名称

 

 

 

sub_topic1 = root_topic1.addSubTopic() # 创建子主题,并设置名称

sub_topic1.setTitle("first sub topic")

 

sub_topic2 = root_topic1.addSubTopic()

sub_topic2.setTitle("second sub topic")

 

sub_topic3 = root_topic1.addSubTopic()

sub_topic3.setTitle("third sub topic")

 

# 除了新建子主题,还可以创建自由主题(注意:只有中心主题支持创建自由主题)

detached_topic1 = root_topic1.addSubTopic(topics_type=TOPIC_DETACHED)

detached_topic1.setTitle("detached topic")

detached_topic1.setPosition(0, 30)

 

# 创建一个子主题的子主题

sub_topic1_1 = sub_topic1.addSubTopic()

sub_topic1_1.setTitle("I'm a sub topic too")

 

second_sheet = workbook.createSheet()   # 创建新画布

second_sheet.setTitle('Second Sheet')

root_topic2 = second_sheet.getRootTopic()

root_topic2.setTitle('Root Node')

 

# 使用其它方式创建子主题元素

topic1 = TopicElement(ownerWorkbook=workbook)

topic1.setTopicHyperlink(first_sheet.getID()) # 为画布创建一个来自第一个画布的主题链接

topic1.setTitle("redirection to the first sheet")

 

topic2 = TopicElement(ownerWorkbook=workbook)

topic2.setTitle("topic with an url hyperlink")

topic2.setURLHyperlink("https://www.cnblogs.com/shouke")  # 为子主题元素设置URL超链接

 

topic3 = TopicElement(ownerWorkbook=workbook)

topic3.setTitle("third node")

topic3.setPlainNotes("notes for this topic")  # 为子主题设置备注 (F4 in XMind)

topic3.setTitle("topic with \n notes")

 

topic4 = TopicElement(ownerWorkbook=workbook)

topic4.setFileHyperlink("d:\\example\demo.jpg")  # 为子主题元素设置文件超链接

topic4.setTitle("topic with a file")

 

topic1_1 = TopicElement(ownerWorkbook=workbook)

topic1_1.setTitle("sub topic")

topic1_1.addLabel("a label")  # 为子主题添加标签(official XMind only can a one label )

# 添加子主题到非中心主题

topic1.addSubTopic(topic1_1)

 

topic1_1_1 = TopicElement(ownerWorkbook=workbook)

topic1_1_1.setTitle("topic can add multiple markers")

# 为主题添加标记

topic1_1_1.addMarker(MarkerId.starBlue)

topic1_1_1.addMarker(MarkerId.flagGreen)

# 为子主题添加子主题

topic1_1.addSubTopic(topic1_1_1)

 

topic2_1 = TopicElement(ownerWorkbook=workbook)

topic2_1.setTitle("topic can add multiple comments")

# 为主题添加评论

topic2_1.addComment("I'm a comment!")

topic2_1.addComment(content="Hello comment!", author='devin')

 

topic2.addSubTopic(topic2_1)

 

# 添加子主题元素到中心主题

root_topic2.addSubTopic(topic1)

root_topic2.addSubTopic(topic2)

root_topic2.addSubTopic(topic3)

root_topic2.addSubTopic(topic4)

 

# 遍历子主题

topics = root_topic2.getSubTopics()

for index, topic in enumerate(topics):

    topic.addMarker("priority-" + str(index + 1)) # 为主题添加标记(优先级图标)

 

# 为子主题1和子主题2创建关系

second_sheet.createRelationship(topic1.getID(), topic2.getID(), "relationship test")

 

# xmind.save(workbook)  # 保存并覆盖原始文件

 

# 仅保存content.xml

# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", only_content=True)  # 不改动原始文件,另存为其它xmind文件

 

# 仅保存content.xml、comments.xml、styles.xml

# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", except_revisions=True)  # 不改动原始文件,另存为其它xmind文件

 

# 保存所有东西,Revisions除外,以节省空间(推荐)

# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", except_revisions=True)  # 不改动原始文件,另存为其它xmind文件

 

# 保存所有内容,并且另存为其它xmind文件(推荐)

xmind.save(workbook=workbook, path='d:\\example\\other.xmind')  # 不改动原始文件,另存为其它xmind文件,等同 xmind.save(workbook, 'd:\\example\\exam.xmind')



运行结果

解析xmind文件

解析xmind文件
#!/usr/bin/env python

# -*- coding:utf-8 -*-

 

import json

import xmind

import pipes

 

def dict_to_prettify_json(data):

    print(json.dumps(data, indent=4, separators=(',', ': ')))

 

 

def custom_parse_xmind(workbook):

    elements = {}

 

    def _echo(tag, element, indent=0):

        title = element.getTitle()

        elements[element.getID()] = title

        print('\t' * indent, tag, ':', pipes.quote(title))

 

    def dump_sheet(sheet):

        root_topic = sheet.getRootTopic()

        _echo('RootTopic', root_topic, 1)

 

        for topic in root_topic.getSubTopics() or []:

            _echo('AttachedSubTopic', topic, 2)

 

        for topic in root_topic.getSubTopics(xmind.core.const.TOPIC_DETACHED) or []:

            _echo('DetachedSubtopic', topic, 2)

 

        for rel in sheet.getRelationships():

            id1, id2 = rel.getEnd1ID(), rel.getEnd2ID()

            print('Relationship: [%s] --> [%s]' % (elements.get(id1), elements.get(id2)))

 

    # 遍历画布

    for sheet in workbook.getSheets():

        _echo('Sheet', sheet)

        dump_sheet(sheet)

 

# 加载已有xmind文件,如果不存在,则新建

workbook = xmind.load('D:\\example\\example.xmind')

print(workbook.getData()) # 获取整个xmind数据(字典的形式)

dict_to_prettify_json(workbook.getData())

 

# 获取某个画布的数据(字典的形式)

first_sheet = workbook.getPrimarySheet()

dict_to_prettify_json(first_sheet.getData())

 

# 获取某个主题数据(字典的形式)

root_topic = first_sheet.getRootTopic()

dict_to_prettify_json(root_topic.getData())

 

# 获取评论数据

commentsbook = workbook.commentsbook

print(commentsbook.getData())

 

# 自定义解析

custom_parse_xmind(workbook)


标签:topic1,脑图,topic,导入,xmind,workbook,path,data
From: https://www.cnblogs.com/chenxdnote/p/18428142

相关文章