# -*- 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 # rgb(223, 132, 0) !important # rgb(239, 4, 42) !important # rgb(78, 180, 52) !important # rgb(0, 145, 255) !important # rgb(122, 95, 255) !important if thisNodeText.find("") > -1: thisNodeColor_ = (223, 132, 0) thisNodeText = thisNodeText.replace("", "") if thisNodeText.find("") > -1: thisNodeColor_ = (239, 4, 42) thisNodeText = thisNodeText.replace("", "") if thisNodeText.find("") > -1: thisNodeColor_ = (78, 180, 52) thisNodeText = thisNodeText.replace("", "") if thisNodeText.find("") > -1: thisNodeColor_ = (0, 145, 255) thisNodeText = thisNodeText.replace("", "") if thisNodeText.find("") > -1: thisNodeColor_ = (122, 95, 255) thisNodeText = thisNodeText.replace("", "") # 判断是否加粗 thisNodeBold = False if thisNodeText.find("") > -1: thisNodeBold = True thisNodeText = thisNodeText.replace("", "") thisNodeText = thisNodeText.replace("", "") thisNodeText = thisNodeText.replace("", "") # 添加标题和段落,采用不同的形式 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("", "") thisNodeNote = thisNodeNote.replace("", "") thisNodeNote = thisNodeNote.replace(" ", " ") thisNodeNote = thisNodeNote.replace("​", "") # (\u200b)0长度字符 (幕布中的那个红点) thisNodeNote = thisNodeNote.replace("
", "\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/', 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 )