123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- from flask import Flask, request, jsonify, Response,send_file,abort
- import os
- import json
- import re
- from datetime import datetime
- app = Flask(__name__)
- # 配置上传文件夹
- UPLOAD_FOLDER = 'uploads'
- if not os.path.exists(UPLOAD_FOLDER):
- os.makedirs(UPLOAD_FOLDER)
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
- from pptx import Presentation
- from pptx.util import Inches,Pt
- from pptx.enum.text import PP_ALIGN,PP_PARAGRAPH_ALIGNMENT
- from pptx.enum.dml import MSO_THEME_COLOR
- from lxml import etree
- import uuid
- def generate_random_key():
- """生成一个 UUID 密钥"""
- return str(uuid.uuid4())
- # 获取当前时间
- def getCurrentDate():
- # 获取当前日期和时间
- current_time = datetime.now()
-
- # 格式化输出
- formatted_time = current_time.strftime("%Y-%m-%d")
- return formatted_time
- # 导出PPT
- def excelToPPT(data):
- # 创建一个新的PPT
- prs = Presentation()
- slide_width = prs.slide_width # 返回EMU单位
- slide_height = prs.slide_height
- font_list = ["latin", "ea", "cs"]
- # 设置主题字体
- for item in data:
- order = item['order']
- title_name = item['title']
- patentNo = item.get('appNo','') or item.get('publicNo','')
- appDate = item.get('appDate','')
- publicDate = item.get('publicDate','')
- appPerson = item.get('appPerson','')
- files = item.get('files',[])
- technical_problem = item.get('technical_problem','')
- technical_means = item.get('technical_means','')
- technical_efficacy = item.get('technical_efficacy','')
- application_field_classification = item.get('application_field_classification','')
- # 添加一个幻灯片
- slide_layout = prs.slide_layouts[5] # 使用一个空白布局
- slide = prs.slides.add_slide(slide_layout)
- # 添加标题
- title = slide.shapes.title
- title.height = Pt(40)
- title.width = slide_width
- title.text = f"{order}、{title_name}-{patentNo}"
- # 设置标题字体
- title_frame = title.text_frame
- title_paragraph = title_frame.paragraphs[0]
- title_paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.LEFT
- title_run = title_paragraph.runs[0]
- # 设置标题字体属性
- title_run.font.name = '微软雅黑'
- title_run.font.size = Pt(20)
- title_run.font.bold = True
- r_element = title_run._r
- rPr = r_element.find('.//a:rPr', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
- if rPr is not None:
- for tag in font_list:
- # 获取或创建 ea 节点
- node = rPr.find(f'.//a:{tag}', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
- if node is None:
- # 如果 ea 节点不存在,创建一个新的 ea 节点
- node = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/main}'+tag)
- rPr.append(node)
- node.set('typeface', '微软雅黑') # 你可以替换为你想要的字体名称
- # 更新 _r 属性
- title_run._r = r_element
- # 添加图片 (替换为实际图片路径)
- if len(files)>0:
- left = Inches(0.5)
- top = Inches(1.3)
- width = Inches(4.5)
- height = Inches(3)
- num = 1
- for image in files:
- if num == 1:
- slide.shapes.add_picture(image, left, top, width, height)
- else:
- left_second_image = Inches(5 * (num - 1))
- slide.shapes.add_picture(image, left_second_image, top, width, height)
- num += 1
- # 添加文本框
- left_text = Inches(0.5)
- top_text = Inches(4.5)
- width_text = slide_width - Inches(0.5).emu
- height_text = Inches(2)
- text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
- text_frame = text_box.text_frame
- # 关键配置:启用自动换行
- text_frame.word_wrap = True
- text_frame.auto_size = None
- paragraph_list = [
- {
- 'field':'申请人',
- 'content':appPerson
- },
- {
- 'field':'申请日',
- 'content':appDate
- },
- {
- 'field':'公开公告日',
- 'content':publicDate
- },
- {
- 'field':'解决问题',
- 'content':technical_problem
- },
- {
- 'field':'技术手段',
- 'content':technical_means
- },
- {
- 'field':'技术效果',
- 'content':technical_efficacy
- },
- {
- 'field':'应用领域',
- 'content':application_field_classification.replace('|','、')
- }
- ]
-
- for item in paragraph_list:
- paragraph = text_frame.add_paragraph()
- paragraph.text = f"{item['field']}:"
- run = paragraph.runs[0]
- run.font.bold = True
- run = paragraph.add_run()
- run.text = item['content']
- for run_item in paragraph.runs:
- run_item.font.size = Pt(15)
- run_item.font.name = '微软雅黑'
- r_element = run_item._r
- rPr = r_element.find('.//a:rPr', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
- if rPr is not None:
- for tag in font_list:
- # 获取或创建 ea 节点
- node = rPr.find(f'.//a:{tag}', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
- if node is None:
- # 如果 ea 节点不存在,创建一个新的 ea 节点
- node = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/main}'+tag)
- rPr.append(node)
- node.set('typeface', '微软雅黑') # 你可以替换为你想要的字体名称
- # 更新 _r 属性
- run_item._r = r_element
- paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.LEFT
- # 保存PPT
- uuid = generate_random_key()
- formatted_time = getCurrentDate()
- file_name = f"result/{formatted_time}_{uuid}.pptx"
- prs.save(file_name)
- return file_name
- # 读取Excel文件
- from openpyxl import load_workbook
- def read_excel_file(file_path):
- try:
- # 读取Excel文件
- # df = pd.read_excel(file_path)
- wb = load_workbook(file_path)
- sheet = wb[wb.sheetnames[0]]
- df = []
- for row in sheet.iter_rows(values_only=True):
- df.append(row)
-
- # 获取所有图片
- images = {}
- for img in sheet._images:
- # 图片位置信息
- anchor = img.anchor
- # 图片数据
- if hasattr(img, '_data'):
- img_data = img._data() if callable(img._data) else img._data
- elif hasattr(img, 'image'):
- from io import BytesIO
- img.image.save(BytesIO(), format='png')
- img_data = BytesIO().getvalue()
- else:
- continue
- images[(anchor._from.row, anchor._from.col)] = img_data
- # 返回数据框
- return df,images
- except Exception as e:
- print(f"读取Excel文件时出错: {e}")
- return None
- # 格式化excel数据
- def getExcelData(excel_data,field_obj={}):
- fieldList = excel_data[0]
- data = []
- for index,item in enumerate(excel_data):
- if index == 0:
- continue
- obj = {}
- for fieldIndex,field in enumerate(fieldList):
- if field in field_obj:
- obj[field_obj[field]] = item[fieldIndex]
- # else:
- # obj[field] = item[fieldIndex]
- data.append(obj)
- return data
- # 读取excel文件
- def read_excel(file_path,current_filename,field_obj):
- #1.读取文件拿到信息
- excel_data,images = read_excel_file(file_path)
-
- data = getExcelData(excel_data,field_obj)
- uuid = generate_random_key()
- # 打印数据
- for i, row in enumerate(data):
- # 检查该行是否有图片
- for (r, c), img_data in images.items():
- if r == i+1:
- # 可以保存图片
- with open(f'image/{uuid}_row_{i+1}_col_{c+1}.png', 'wb') as f:
- f.write(img_data)
- if 'files' not in row:
- row['files'] = []
- row['files'].append(f'image/{uuid}_row_{i+1}_col_{c+1}.png')
- return data
-
- # 根据excel导出PPT接口
- @app.route(f'/api/exportPPT', methods=['POST'])
- def exportPPT():
- # 检查请求中是否有文件
- if 'file' not in request.files:
- return jsonify({'error': 'No file part'}), 400
- file = request.files['file']
- filename = ''
- # 检查是否选择了文件
- if file.filename == '':
- return jsonify({'error': 'No selected file'}), 400
- if file:
- current_filename = file.filename
- # filename = f'uploads\{current_filename}'
- # if os.path.exists(filename):
- # pass
- # else:
- # 安全地获取文件名
- filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
- # 确保不会覆盖现有文件
- counter = 1
- while os.path.exists(filename):
- name, ext = os.path.splitext(file.filename)
- filename = os.path.join(app.config['UPLOAD_FOLDER'], f"{name}_{counter}{ext}")
- counter += 1
- # 保存文件
- file.save(filename)
- # 文件存在时直接处理
- file_path = ''
- # 1. 获取文件内容
- field_obj = {
- '公开(公告)号':'publicNo',
- '摘要附图':'image',
- '序号':'order',
- '标题':'title',
- '申请号':'appNo',
- '申请日':'appDate',
- '当前申请(专利权)人':'appPerson',
- '公开(公告)日':'publicDate',
- '技术手段':'technical_means',
- '技术功效':'technical_efficacy',
- '应用领域分类':'application_field_classification',
- '技术问题':'technical_problem',
- }
- excel_data = read_excel(filename,current_filename,field_obj)
- if excel_data:
- # 2. 生成并导出PPT
- file_path = excelToPPT(excel_data)
- else:
- return jsonify({'error': 'No file part'}), 400
-
- return jsonify(file_path) ,200
- #下载文件
- @app.route(f'/api/download',methods=['GET'])
- async def download():
- get_params = request.args
- filename = get_params.get('filePath')
- file_path = os.path.join(os.getcwd(), '', filename)
- if not os.path.exists(file_path):
- abort(404) # 返回 404 错误
-
- try:
- # 返回文件给用户下载
- return send_file(file_path, as_attachment=True)
- except Exception as e:
- # 处理可能的异常(例如文件权限问题)
- return str(e), 500
- # 根据excel导出xmind接口
- @app.route(f'/api/exportXmind', methods=['POST'])
- def exportXmind():
- # 检查请求中是否有文件
- if 'file' not in request.files:
- return jsonify({'error': 'No file part'}), 400
- file = request.files['file']
- filename = ''
- # 检查是否选择了文件
- if file.filename == '':
- return jsonify({'error': 'No selected file'}), 400
- if file:
- current_filename = file.filename
- # filename = f'uploads\{current_filename}'
- # if os.path.exists(filename):
- # pass
- # else:
- # 安全地获取文件名
- filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
- # 确保不会覆盖现有文件
- counter = 1
- while os.path.exists(filename):
- name, ext = os.path.splitext(file.filename)
- filename = os.path.join(app.config['UPLOAD_FOLDER'], f"{name}_{counter}{ext}")
- counter += 1
- # 保存文件
- file.save(filename)
- # 文件存在时直接处理
- file_path = ''
- # 分类节点
- classify_nodes = [
- {
- 'name':'AI智能化',
- 'path':'AI智能化',
- 'parent_name':'',
- 'order':1
- },
- {
- 'name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_name':'AI智能化',
- 'parent_order':1,
- 'order':2
- },
- {
- 'name':'功能控制',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\功能控制',
- 'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_order':2,
- 'order':3
- },
- {
- 'name':'语音控制',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\语音控制',
- 'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_order':2,
- 'order':4
- },
- {
- 'name':'手势识别',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\手势识别',
- 'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_order':2,
- 'order':5
- },
- {
- 'name':'佩戴检测',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\佩戴检测',
- 'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_order':2,
- 'order':6
- },
- {
- 'name':'其它',
- 'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\其它',
- 'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
- 'parent_order':2,
- 'order':7
- },
- {
- 'name':'音频信号处理',
- 'path':'AI智能化\音频信号处理',
- 'parent_name':'AI智能化',
- 'parent_order':1,
- 'order':8
- },
- {
- 'name':'音质',
- 'path':'AI智能化\音频信号处理\音质',
- 'parent_name':'音频信号处理',
- 'parent_order':8,
- 'order':9
- },
- {
- 'name':'空间音频',
- 'path':'AI智能化\音频信号处理\空间音频',
- 'parent_name':'音频信号处理',
- 'parent_order':8,
- 'order':10
- },
- {
- 'name':'降噪',
- 'path':'AI智能化\音频信号处理\降噪',
- 'parent_name':'音频信号处理',
- 'parent_order':8,
- 'order':11
- },
- {
- 'name':'高清编码',
- 'path':'AI智能化\音频信号处理\高清编码',
- 'parent_name':'音频信号处理',
- 'parent_order':8,
- 'order':12
- },
- {
- 'name':'其它',
- 'path':'AI智能化\音频信号处理\其它',
- 'parent_name':'音频信号处理',
- 'parent_order':8,
- 'order':13
- },
- {
- 'name':'大模型简化',
- 'path':'AI智能化\大模型简化',
- 'parent_name':'AI智能化',
- 'parent_order':1,
- 'order':14
- },
- {
- 'name':'智能化应用',
- 'path':'AI智能化\智能化应用',
- 'parent_name':'AI智能化',
- 'parent_order':1,
- 'order':15
- },
- {
- 'name':'翻译',
- 'path':'AI智能化\智能化应用\翻译',
- 'parent_name':'智能化应用',
- 'parent_order':15,
- 'order':16
- },
- {
- 'name':'会议纪要',
- 'path':'AI智能化\智能化应用\会议纪要',
- 'parent_name':'智能化应用',
- 'parent_order':15,
- 'order':17
- },
- {
- 'name':'其它',
- 'path':'AI智能化\智能化应用\其它',
- 'parent_name':'智能化应用',
- 'parent_order':15,
- 'order':18
- },
- {
- 'name':'运动/健康信号处理',
- 'path':'运动/健康信号处理',
- 'parent_name':'',
- 'order':19
- },
- {
- 'name':'传感器元件',
- 'path':'运动/健康信号处理\传感器元件',
- 'parent_name':'运动/健康信号处理',
- 'parent_order':19,
- 'order':20
- },
- {
- 'name':'健康状态识别与分析',
- 'path':'运动/健康信号处理\健康状态识别与分析',
- 'parent_name':'运动/健康信号处理',
- 'parent_order':19,
- 'order':21
- },
- {
- 'name':'无线技术',
- 'path':'无线技术',
- 'parent_name':'',
- 'order':22
- },
- {
- 'name':'蓝牙/WiFi协议、连接',
- 'path':'无线技术\蓝牙/WiFi协议、连接',
- 'parent_name':'无线技术',
- 'parent_order':22,
- 'order':23
- },
- {
- 'name':'低延迟',
- 'path':'无线技术\蓝牙/WiFi协议、连接\低延迟',
- 'parent_name':'蓝牙/WiFi协议、连接',
- 'parent_order':23,
- 'order':24
- },
- {
- 'name':'其它',
- 'path':'无线技术\蓝牙/WiFi协议、连接\其它',
- 'parent_name':'蓝牙/WiFi协议、连接',
- 'parent_order':23,
- 'order':25
- },
- {
- 'name':'信号传输质量',
- 'path':'无线技术\信号传输质量',
- 'parent_name':'无线技术',
- 'parent_order':22,
- 'order':26
- },
- {
- 'name':'高可靠性',
- 'path':'无线技术\信号传输质量\高可靠性',
- 'parent_name':'信号传输质量',
- 'parent_order':26,
- 'order':27
- },
- {
- 'name':'其它',
- 'path':'无线技术\信号传输质量\其它',
- 'parent_name':'信号传输质量',
- 'parent_order':26,
- 'order':28
- },
- {
- 'name':'天线',
- 'path':'无线技术\天线',
- 'parent_name':'无线技术',
- 'parent_order':22,
- 'order':29
- },
- {
- 'name':'结构/材料/工艺',
- 'path':'结构/材料/工艺',
- 'parent_name':'',
- 'order':30
- },
- {
- 'name':'MIC/扬声器',
- 'path':'结构/材料/工艺\MIC/扬声器',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':31
- },
- {
- 'name':'舒适度',
- 'path':'结构/材料/工艺\舒适度',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':32
- },
- {
- 'name':'封装',
- 'path':'结构/材料/工艺\封装',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':33
- },
- {
- 'name':'电池',
- 'path':'结构/材料/工艺\电池',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':34
- },
- {
- 'name':'可调节结构/可拆卸结构/气囊结构',
- 'path':'结构/材料/工艺\可调节结构/可拆卸结构/气囊结构',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':35
- },
- {
- 'name':'传感器件与产品耦合的材料/结构',
- 'path':'结构/材料/工艺\传感器件与产品耦合的材料/结构',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':36
- },
- {
- 'name':'耳机特殊形态',
- 'path':'结构/材料/工艺\耳机特殊形态',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':37
- },
- {
- 'name':'其它',
- 'path':'结构/材料/工艺\其它',
- 'parent_name':'结构/材料/工艺',
- 'parent_order':30,
- 'order':38
- },
- {
- 'name':'其它',
- 'path':'其它',
- 'parent_name':'',
- 'order':39
- }
- ]
- # 1. 获取文件内容
- field_obj = {
- '公开(公告)号':'publicNo',
- '摘要附图':'image',
- '序号':'order',
- '标题':'title',
- '申请号':'appNo',
- '申请日':'appDate',
- '当前申请(专利权)人':'appPerson',
- '公开(公告)日':'publicDate',
- '分类':'classify',
- '法律状态/事件':'status',
- '工作空间注释':'annotation'
- }
- excel_data = read_excel(filename,current_filename,field_obj)
- if excel_data:
- # 2. 获取待导出的数据结构
- exportXmindData = getExportXmindData(excel_data,classify_nodes)
- # 3. 生成并导出xmind
- file_path = excelToXmind(exportXmindData)
- else:
- return jsonify({'error': 'No file part'}), 400
- return jsonify(file_path) ,200
- # 获取待导出的数据结构
- def getExportXmindData(excel_data,classify_nodes):
-
- for item in excel_data:
- if 'classify' in item and item['classify']:
- item['classify_list'] = item['classify'].split('>')
- # 将专利添加到分类节点中去
- for node_item in classify_nodes:
- path = node_item['path']
- for item in excel_data:
- if 'classify_list' in item and item['classify_list']:
- classify_list = item['classify_list']
- if path in classify_list:
- if 'patents' not in node_item:
- node_item['patents'] = []
- node_item['patents'].append(item)
- node_dict = {node['order']: node for node in classify_nodes}
- tree = []
- # 生成树级结构
- for node in classify_nodes:
- if node['parent_name'] == '':
- # 如果没有父节点,说明是根节点
- tree.append(node)
- else:
- # 否则,找到父节点并添加当前节点为子节点
- parent = node_dict.get(node['parent_order'])
- if parent:
- if 'children' not in parent:
- parent['children'] = []
- parent['children'].append(node)
- return tree
- import shutil
- # 复制模板文件
- def copy_file_with_relative_path(relative_source_path, destination_path):
- try:
- # 获取当前工作目录
- current_dir = os.getcwd()
-
- # 构建完整的源文件路径
- full_source_path = os.path.join(current_dir, relative_source_path)
-
- # 检查源文件是否存在
- if not os.path.exists(full_source_path):
- raise FileNotFoundError(f"源文件不存在:{full_source_path}")
-
- # 使用 shutil.copy2 复制文件,同时保留元数据
- shutil.copy2(full_source_path, destination_path)
-
- print(f"文件已成功复制到:{destination_path}")
- except Exception as e:
- print(f"复制文件时发生错误:{e}")
- # 导出xmind
- def excelToXmind(tree):
- # 1. 创建空白xmind文件
- uuid = generate_random_key()
- formatted_time = getCurrentDate()
- file_name = f"result/{formatted_time}_{uuid}.xmind"
- create_xmind_with_images(file_name,tree)
- return file_name
- import os
- import zipfile
- import xml.etree.ElementTree as ET
- from xml.dom import minidom
- def create_xmind_with_images(output_file,data):
- """创建包含三级节点和图片的 XMind 文件"""
- # 临时工作目录
- temp_dir = "xmind_temp"
-
- # 清理并创建目录结构
- if os.path.exists(temp_dir):
- shutil.rmtree(temp_dir)
- os.makedirs(os.path.join(temp_dir, "content"))
- os.makedirs(os.path.join(temp_dir, "META-INF"))
- os.makedirs(os.path.join(temp_dir, "resources"))
- os.makedirs(os.path.join(temp_dir, "Thumbnails"))
-
- create_metadata_json(temp_dir)
- # 1. 创建 content.xml 文件
- images_list = create_content_xml(temp_dir,data)
-
- # 2. 创建 manifest.xml 文件
- create_manifest_xml(temp_dir,images_list)
-
- # 3. 添加示例图片到 resources 目录 (实际使用中替换为你的图片)
- create_sample_images(temp_dir,images_list)
-
- # 4. 打包成 XMind 文件
- with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
- for root, _, files in os.walk(temp_dir):
- for file in files:
- file_path = os.path.join(root, file)
- arcname = os.path.relpath(file_path, temp_dir)
- zipf.write(file_path, arcname)
-
- # 5. 清理临时文件
- shutil.rmtree(temp_dir)
- print(f"XMind 文件已创建: {output_file}")
- return output_file
- def create_metadata_json(temp_dir):
- """创建 XMind 2022 的 metadata.json"""
- metadata_content = '''{
- "activeSheetId": "sheet1",
- "creator": {
- "name": "XMind Python Generator",
- "version": "1.0"
- },
- "fileStructure": {
- "markerIds": [],
- "numberingFormat": null,
- "theme": "Default",
- "version": "2.0"
- },
- "modifiedBy": "XMind Python Generator"
- }'''
-
- # 写入文件
- metadata_path = os.path.join(temp_dir, "metadata.json")
- with open(metadata_path, 'w', encoding='utf-8') as f:
- f.write(metadata_content)
- def add_topic_recursive(parent_element, node_data, ns,num,images_list):
- """
- 递归添加主题节点
-
- 参数:
- parent_element: 父XML元素
- node_data: 当前节点数据
- ns: XML命名空间
- """
- node_name = ''
- classify_name = node_data.get('name','')
- if not classify_name:#不是分类节点,是专利
- patentNo = node_data.get('appNo','') or node_data.get('publicNo','')
- title = node_data.get('title','')
- status = node_data.get('status','')
- annotation = node_data.get('annotation','')
- node_name = node_name + patentNo
- if title:
- node_name = node_name + f'({title})'
- node_name = node_name + status + '\n'
- node_name = node_name + annotation
- else:
- node_name = classify_name
- # 创建当前主题节点
- topic = ET.SubElement(parent_element, "topic", id=f"topic{num}_{generate_random_key()}")
- ET.SubElement(topic, "title").text = node_name
-
- # 添加图片(如果存在)
- if 'files' in node_data:
- for file in node_data['files']:
- file_index = file.rfind('/')
- file_name = file[file_index+1:]
- images_list.append(file_name)
- image_elem = ET.SubElement(topic, "image", src=f"resources/{file_name}", align="center")
- image_elem.set("width", "200")
- image_elem.set("height", "150")
- break
-
- # 如果有子节点,递归添加
- if "children" in node_data and node_data["children"]:
- num += 1
- children_container = ET.SubElement(topic, "children")
- topics_container = ET.SubElement(children_container, "topics", type="attached")
- for child in node_data["children"]:
- add_topic_recursive(topics_container, child, ns,num,images_list)
- # 如果有子节点,递归添加
- if "patents" in node_data and node_data["patents"]:
- num += 1
- children_container = ET.SubElement(topic, "children")
- topics_container = ET.SubElement(children_container, "topics", type="attached")
- for child in node_data["patents"]:
- add_topic_recursive(topics_container, child, ns,num,images_list)
- def create_content_xml(temp_dir,data):
- """创建 content.xml 文件"""
- # XML 命名空间
- ns = "urn:xmind:xmap:xmlns:content:2.0"
-
- # 创建根元素
- xmap_content = ET.Element("xmap-content", xmlns=ns, version="2.0")
-
- # 创建工作表
- sheet = ET.SubElement(xmap_content, "sheet", id="sheet1")
-
- # 创建主题
- topic = ET.SubElement(sheet, "topic", id="root", timestamp="0",structure="org.xmind.ui.logic.right")
- ET.SubElement(topic, "title").text = "**厂商**品类的专利布局"
- structure = ET.SubElement(topic, "structure")
- structure.set("type", "str")
- structure.text = "org.xmind.ui.logic.right"
-
- # 添加子节点容器
- children_container = ET.SubElement(topic, "children")
- topics_container = ET.SubElement(children_container, "topics", type="attached")
- images_list = []
- # 递归添加子节点
- for child in data:
- add_topic_recursive(topics_container, child, ns,1,images_list)
- # 美化XML输出
- xml_str = ET.tostring(xmap_content, encoding='utf-8')
- pretty_xml = minidom.parseString(xml_str).toprettyxml(indent=" ")
- pretty_xml = pretty_xml.replace('<?xml version="1.0" ?>', '<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
-
- # 写入文件
- content_path = os.path.join(temp_dir, "content.xml")
- with open(content_path, 'w', encoding='utf-8') as f:
- f.write(pretty_xml)
- return images_list
- def create_topic(parent, title, topic_id):
- """创建主题节点"""
- topic = ET.SubElement(parent, "topic", id=topic_id)
- ET.SubElement(topic, "title").text = title
- return topic
- def create_manifest_xml(temp_dir,images_list):
- """创建 manifest.xml 文件"""
- str = ''
- if images_list:
- for file in images_list:
- str = str + f'<file-entry full-path="resources/{file}" media-type="image/png"/>' + '\n'
- manifest_content = f'''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0">
- <file-entry full-path="content.xml" media-type="text/xml"/>
- <file-entry full-path="META-INF/" media-type=""/>
- <file-entry full-path="META-INF/manifest.xml" media-type="text/xml"/>
- <file-entry full-path="resources/" media-type=""/>
- {str}
- </manifest>'''
-
- manifest_path = os.path.join(temp_dir, "META-INF", "manifest.xml")
- with open(manifest_path, 'w', encoding='utf-8') as f:
- f.write(manifest_content)
- return True
- def create_sample_images(temp_dir,images_list):
- """创建示例图片(实际使用中应替换为你的图片)"""
- # 创建两个示例图片(实际项目中应使用真实图片)
- resources_dir = os.path.join(temp_dir, "resources")
-
- if images_list:
- for file in images_list:
- shutil.copy(f"image/{file}", os.path.join(resources_dir, file))
- return True
- from xmindparser import xmind_to_xml
- def etxe1():
- xml = xmind_to_xml('result/2025-06-26_2906a7b1-baf4-4b09-b668-b80c3a8264cb.xmind')
- print(xml)
- if __name__ == '__main__':
- # etxe1()
- app.run(debug=True, host='0.0.0.0', port=2500)
|