|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
import json
|
|
|
import os
|
|
|
import time
|
|
|
from urllib.parse import quote
|
|
|
|
|
|
import requests
|
|
|
from docx import Document
|
|
|
from docx.oxml.ns import qn
|
|
|
from docx.shared import Inches, Pt, RGBColor
|
|
|
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
|
|
|
|
|
from flask import Flask, send_file, render_template
|
|
|
from flask_cors import CORS
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
MUBU_HOST = "https://mubu.com/"
|
|
|
ROOT = os.path.abspath('.') # 表示当前所处的文件夹的绝对路径
|
|
|
|
|
|
|
|
|
# 获取当前日期
|
|
|
def get_current_day():
|
|
|
return time.strftime('%Y-%m-%d', time.localtime(time.time()))
|
|
|
|
|
|
|
|
|
# 获取当前日期时间
|
|
|
def get_current_time():
|
|
|
return time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
|
|
|
|
|
|
|
|
|
# 获取两个字符之间的
|
|
|
def get_str_btw(s, f, b):
|
|
|
par = s.partition(f)
|
|
|
return (par[2].partition(b))[0][:]
|
|
|
|
|
|
|
|
|
# 网络请求
|
|
|
def getResponse(url):
|
|
|
print("url = " + url)
|
|
|
response = requests.get(url, timeout=30) # 请求超时时间为10秒
|
|
|
response.raise_for_status()
|
|
|
# http请求的返回状态,若为200则表示请求成功,返回的状态码是 int类型
|
|
|
code = response.status_code
|
|
|
if code == 200:
|
|
|
return response
|
|
|
else:
|
|
|
return None
|
|
|
|
|
|
|
|
|
# 判断是否为json
|
|
|
def isJson(jsonStr):
|
|
|
"""判断是否为json对象"""
|
|
|
try:
|
|
|
json.loads(jsonStr)
|
|
|
except Exception as e:
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
|
|
|
# 获取颜色RGB
|
|
|
def color(value):
|
|
|
digit = list(map(str, range(10))) + list("abcdef")
|
|
|
if isinstance(value, tuple):
|
|
|
string = '#'
|
|
|
for i in value:
|
|
|
a1 = i // 16
|
|
|
a2 = i % 16
|
|
|
string += digit[a1] + digit[a2]
|
|
|
return string
|
|
|
elif isinstance(value, str):
|
|
|
a1 = digit.index(value[1]) * 16 + digit.index(value[2])
|
|
|
a2 = digit.index(value[3]) * 16 + digit.index(value[4])
|
|
|
a3 = digit.index(value[5]) * 16 + digit.index(value[6])
|
|
|
return (a1, a2, a3)
|
|
|
|
|
|
|
|
|
# 保存图片到本地
|
|
|
def get_image(url, pic_name):
|
|
|
"""
|
|
|
:param pic_name:
|
|
|
:param url: 图片链接参数
|
|
|
:return: 无返回值,直接保存一张图片
|
|
|
"""
|
|
|
try:
|
|
|
response = requests.get(url)
|
|
|
with open(pic_name, "wb") as fp:
|
|
|
for data in response.iter_content(128):
|
|
|
fp.write(data)
|
|
|
return True
|
|
|
except Exception as e:
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 生成文档
|
|
|
def generateDocx(doc, dic, level):
|
|
|
# print("--------------------------------------------------")
|
|
|
# print(dic)
|
|
|
if dic is not None and isinstance(dic, list) and len(dic) > 0:
|
|
|
for dicIndex in range(len(dic)):
|
|
|
# print("level=" + str(level) + "; len=" + str(len(dic)) + "; i = " + str(dicIndex))
|
|
|
thisNode = dic[dicIndex]
|
|
|
if isinstance(thisNode, dict):
|
|
|
thisNodeHeading = thisNode.get("heading")
|
|
|
thisNodeText = thisNode.get("text")
|
|
|
if thisNodeText == "评语保存":
|
|
|
thisNodeText
|
|
|
|
|
|
thisNodeColor = thisNode.get("color")
|
|
|
thisNodeNote = thisNode.get("note")
|
|
|
thisNodeCollapsed = thisNode.get("collapsed")
|
|
|
thisNodeFinish = thisNode.get("finish")
|
|
|
thisNodeImages = thisNode.get("images")
|
|
|
|
|
|
# print(thisNodeHeading, thisNodeText)
|
|
|
|
|
|
# 处理缺失的值
|
|
|
thisNodeText = thisNodeText is None and "" or thisNodeText
|
|
|
# 处理颜色
|
|
|
thisNodeColor_ = (0, 0, 0)
|
|
|
if thisNodeColor is not None and thisNodeColor is not "":
|
|
|
thisNodeColor_ = color(thisNodeColor)
|
|
|
|
|
|
# V3版本 颜色使用了 class
|
|
|
# <span class=" text-color-yellow"> rgb(223, 132, 0) !important
|
|
|
# <span class=" text-color-red"> rgb(239, 4, 42) !important
|
|
|
# <span class=" text-color-green"> rgb(78, 180, 52) !important
|
|
|
# <span class=" text-color-blue"> rgb(0, 145, 255) !important
|
|
|
# <span class=" text-color-purple"> rgb(122, 95, 255) !important
|
|
|
if thisNodeText.find("<span class=\" text-color-yellow\">") > -1:
|
|
|
thisNodeColor_ = (223, 132, 0)
|
|
|
thisNodeText = thisNodeText.replace("<span class=\" text-color-yellow\">", "")
|
|
|
if thisNodeText.find("<span class=\" text-color-red\">") > -1:
|
|
|
thisNodeColor_ = (239, 4, 42)
|
|
|
thisNodeText = thisNodeText.replace("<span class=\" text-color-red\">", "")
|
|
|
if thisNodeText.find("<span class=\" text-color-green\">") > -1:
|
|
|
thisNodeColor_ = (78, 180, 52)
|
|
|
thisNodeText = thisNodeText.replace("<span class=\" text-color-green\">", "")
|
|
|
if thisNodeText.find("<span class=\" text-color-blue\">") > -1:
|
|
|
thisNodeColor_ = (0, 145, 255)
|
|
|
thisNodeText = thisNodeText.replace("<span class=\" text-color-blue\">", "")
|
|
|
if thisNodeText.find("<span class=\" text-color-purple\">") > -1:
|
|
|
thisNodeColor_ = (122, 95, 255)
|
|
|
thisNodeText = thisNodeText.replace("<span class=\" text-color-purple\">", "")
|
|
|
|
|
|
# 判断是否加粗
|
|
|
thisNodeBold = False
|
|
|
if thisNodeText.find("<span class=\"bold\">") > -1:
|
|
|
thisNodeBold = True
|
|
|
thisNodeText = thisNodeText.replace("<span class=\"bold\">", "")
|
|
|
|
|
|
thisNodeText = thisNodeText.replace("<span>", "")
|
|
|
thisNodeText = thisNodeText.replace("</span>", "")
|
|
|
|
|
|
# 添加标题和段落,采用不同的形式
|
|
|
if thisNodeHeading is not None and int(thisNodeHeading) and int(thisNodeHeading) != 0:
|
|
|
paragraph = doc.add_heading('', level=int(level))
|
|
|
else:
|
|
|
paragraph = doc.add_paragraph('', style='List Bullet')
|
|
|
|
|
|
paragraph_run = paragraph.add_run(thisNodeText)
|
|
|
paragraph_run.font.name = u'Times New Roman'
|
|
|
paragraph_run.element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
|
|
|
# print(thisNodeColor_)
|
|
|
paragraph_run.font.color.rgb = RGBColor(thisNodeColor_[0], thisNodeColor_[1], thisNodeColor_[2])
|
|
|
if thisNodeBold:
|
|
|
paragraph_run.font.bold = True # 加粗
|
|
|
|
|
|
paragraph_format = paragraph.paragraph_format
|
|
|
paragraph_format.left_indent = Inches(0.15 * (level - 1)) # 调整左缩进0.3英寸
|
|
|
paragraph_format.space_before = Pt(0) # 上行间距
|
|
|
paragraph_format.space_after = Pt(0) # 下行间距
|
|
|
paragraph_format.line_spacing = Pt(14*1.3) # 行距
|
|
|
|
|
|
# document.add_paragraph('Intese quote', style="Intense Quote")
|
|
|
# document.add_paragraph('first item in ordered list', style='List Number')
|
|
|
|
|
|
# 处理备注
|
|
|
if thisNodeNote is not None and thisNodeNote != "":
|
|
|
# if isJson(thisNodeNote):
|
|
|
# thisNodeNote = str(json.dumps(thisNodeNote, indent=2))
|
|
|
thisNodeNote = thisNodeNote.replace("<span>", "")
|
|
|
thisNodeNote = thisNodeNote.replace("</span>", "")
|
|
|
thisNodeNote = thisNodeNote.replace(" ", " ")
|
|
|
thisNodeNote = thisNodeNote.replace("", "") # (\u200b)0长度字符 (幕布中的那个红点)
|
|
|
thisNodeNote = thisNodeNote.replace("<br>", "\n")
|
|
|
thisNodeNote = thisNodeNote.replace("\" ", "\"")
|
|
|
thisNodeNote = thisNodeNote.replace(" \"", "\"")
|
|
|
thisNodeNote = thisNodeNote.replace("\",", "\",\n")
|
|
|
thisNodeNote = thisNodeNote.replace("\n\n", "\n")
|
|
|
thisNodeNote = thisNodeNote.replace("\n\n", "\n")
|
|
|
thisNodeNote = thisNodeNote.replace("\n\n", "\n")
|
|
|
thisNodeNote = thisNodeNote.replace("\n\n", "\n")
|
|
|
#thisNodeNote = thisNodeNote.replace(" ", "")
|
|
|
thisNodeNoteParagraph = doc.add_paragraph()
|
|
|
thisNodeNoteParagraph.paragraph_format.left_indent = Inches(0.1 * (level - 1)) # 调整左缩进0.3英寸
|
|
|
note_run = thisNodeNoteParagraph.add_run(thisNodeNote)
|
|
|
note_run.font.color.rgb = RGBColor(105, 105, 105)
|
|
|
note_run.font.italic = True # 斜体
|
|
|
note_run.font.size = Pt(10)
|
|
|
else:
|
|
|
pass
|
|
|
|
|
|
# 处理图片
|
|
|
if thisNodeImages is not None:
|
|
|
# print(thisNodeImages)
|
|
|
# print(len(thisNodeImages))
|
|
|
if isinstance(thisNodeImages, list) and len(thisNodeImages) > 0:
|
|
|
for img in range(len(thisNodeImages)):
|
|
|
thisNodeImage = thisNodeImages[img]
|
|
|
uri = thisNodeImage.get("uri")
|
|
|
if uri is not None:
|
|
|
img_src = "https://api2.mubu.com/v3/" + uri
|
|
|
file_name = uri.replace("document_image/", "")
|
|
|
# 将远程数据下载到本地,第二个参数就是要保存到本地的文件名
|
|
|
if get_image(img_src, file_name):
|
|
|
doc.add_picture(file_name, width=Inches(4)) # 添加图, 设置宽度
|
|
|
os.remove(file_name) # 删除临时文件
|
|
|
else:
|
|
|
pass
|
|
|
else:
|
|
|
pass
|
|
|
else:
|
|
|
pass
|
|
|
|
|
|
# 处理子节点
|
|
|
thisNodeChildren = thisNode.get("children")
|
|
|
if thisNodeChildren is not None:
|
|
|
generateDocx(doc, thisNodeChildren, level + 1)
|
|
|
else:
|
|
|
pass
|
|
|
else:
|
|
|
pass
|
|
|
else:
|
|
|
pass
|
|
|
|
|
|
|
|
|
# 生成文档封皮
|
|
|
def generatePage(doc, docName):
|
|
|
# 设置页眉 页脚
|
|
|
# sections = document.sections
|
|
|
section = doc.sections[0]
|
|
|
# 页眉
|
|
|
header = section.header
|
|
|
header_paragraph = header.paragraphs[0]
|
|
|
header_paragraph.text = "\n\t东师理想慧办公系统接口文档"
|
|
|
# 页脚
|
|
|
# footer = section.footer
|
|
|
# footer_paragraph = footer.paragraphs[0]
|
|
|
# footer_paragraph.text = ""
|
|
|
|
|
|
# 设置封皮 居中
|
|
|
fpParagraph = doc.add_paragraph()
|
|
|
fpParagraph.add_run("\n").font.size = Pt(44)
|
|
|
fpParagraph.add_run("\n").font.size = Pt(44)
|
|
|
fpParagraph.add_run("\n").font.size = Pt(44)
|
|
|
|
|
|
fpParagraph = doc.add_paragraph()
|
|
|
fpParagraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
|
fpParagraph_run = fpParagraph.add_run(docName)
|
|
|
fpParagraph_run.font.size = Pt(28)
|
|
|
fpParagraph_run.font.bold = True # 加粗
|
|
|
fpParagraph_run.font.name = 'Times New Roman'
|
|
|
fpParagraph_run.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
|
|
|
|
|
fpParagraph = doc.add_paragraph()
|
|
|
fpParagraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
|
fpParagraph_run = fpParagraph.add_run("系统接口文档\n")
|
|
|
fpParagraph_run.font.size = Pt(22)
|
|
|
fpParagraph_run.font.bold = True # 加粗
|
|
|
fpParagraph_run.font.name = 'Times New Roman'
|
|
|
fpParagraph_run.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
|
|
|
|
|
# 设置分页
|
|
|
doc.add_page_break()
|
|
|
|
|
|
# 设置标题 居中
|
|
|
title = doc.add_heading(level=0)
|
|
|
title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
|
title_run = title.add_run(docName)
|
|
|
title_run.font.size = Pt(14)
|
|
|
title_run.font.name = 'Times New Roman'
|
|
|
title_run.element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
|
|
|
|
|
|
# 设置目录位置 居中
|
|
|
muluParagraph = doc.add_paragraph()
|
|
|
# muluParagraph.paragraph_format.left_indent = Inches(0.1 * (level - 1)) # 调整左缩进0.3英寸
|
|
|
mulu_run = muluParagraph.add_run("[请在此处手动添加目录]")
|
|
|
mulu_run.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
|
mulu_run.font.color.rgb = RGBColor(105, 105, 105)
|
|
|
mulu_run.font.italic = True # 斜体
|
|
|
mulu_run.font.size = Pt(10)
|
|
|
|
|
|
|
|
|
def generater(key):
|
|
|
mbUrl = "https://api2.mubu.com/v3/doc/" + key
|
|
|
mbResponse = getResponse(mbUrl)
|
|
|
if mbResponse is not None:
|
|
|
text = mbResponse.text
|
|
|
# print(text)
|
|
|
docId = get_str_btw(text, "\"doc\":{\"id\":\"", "\",\"")
|
|
|
print("docId = " + docId)
|
|
|
docUrl = "https://mubu.com/api/document/view/get?docId=" + docId
|
|
|
mbDocResponse = getResponse(docUrl)
|
|
|
if mbDocResponse is not None:
|
|
|
mbDocJsonText = mbDocResponse.text
|
|
|
# print(mbDocJsonText)
|
|
|
try:
|
|
|
mbDocJson = json.loads(mbDocJsonText)
|
|
|
if mbDocJson['msg'] == "Success":
|
|
|
docName = mbDocJson['data']['name']
|
|
|
definition = mbDocJson['data']['definition']
|
|
|
definitionJson = json.loads(definition)
|
|
|
nodeList = definitionJson["nodes"]
|
|
|
|
|
|
# print(nodeList)
|
|
|
|
|
|
document = Document()
|
|
|
# 设置一个空白样式
|
|
|
style = document.styles['Normal']
|
|
|
# 设置默认字号
|
|
|
style.font.size = Pt(11)
|
|
|
# 设置西文字体
|
|
|
style.font.name = 'Times New Roman'
|
|
|
# 设置中文字体
|
|
|
style.element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
|
|
|
# 获取段落样式
|
|
|
default_paragraph_format = style.paragraph_format
|
|
|
# 首行缩进0.74厘米,即2个字符
|
|
|
# default_paragraph_format.first_line_indent = Cm(0.74)
|
|
|
# 生成文档封皮
|
|
|
generatePage(document, docName)
|
|
|
# 生成文档
|
|
|
generateDocx(document, nodeList, 1)
|
|
|
# 文件位置
|
|
|
file_path = '%s_%s.docx' % (docName, get_current_time())
|
|
|
# 保存
|
|
|
document.save(file_path)
|
|
|
print("文件生成完成")
|
|
|
return file_path
|
|
|
else:
|
|
|
print("返回值错误")
|
|
|
return None
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
return None
|
|
|
else:
|
|
|
print("无响应")
|
|
|
return None
|
|
|
else:
|
|
|
print("无响应")
|
|
|
return None
|
|
|
|
|
|
|
|
|
@app.route('/mbdoc/<key>', methods=['GET'])
|
|
|
def index(key):
|
|
|
# url = request.args.get("url")
|
|
|
# mbUrl = "https://mubu.com/doc/5_8huyn130"
|
|
|
file_name = generater(key)
|
|
|
if file_name is not None:
|
|
|
filename = quote(file_name)
|
|
|
rv = send_file(ROOT + "\\" + file_name, as_attachment=True, attachment_filename=filename)
|
|
|
rv.headers['Content-Disposition'] += "; filename*=utf-8''{}".format(filename)
|
|
|
return rv
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
CORS(app, supports_credentials=True)
|
|
|
app.run(
|
|
|
host='0.0.0.0',
|
|
|
port=8080,
|
|
|
debug=False
|
|
|
)
|