# mrdocTools **Repository Path**: atyin/mrdocTools ## Basic Information - **Project Name**: mrdocTools - **Description**: mrdocTools适用于觅思文档的python自动化脚本 - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2022-08-25 - **Last Updated**: 2023-03-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mrdoctools # 适用于[觅思文档](https://gitee.com/zmister/MrDoc/tree/master#%E8%A7%85%E6%80%9D%E6%96%87%E6%A1%A3%E5%BC%80%E6%BA%90%E7%89%88)的本地文档同步工具 ### 简介: ### 本项目基于mrdoc开源文档的api实现了 #### ```push本地仓库中的文档至远程mrdoc平台``` #### ```push前自动查找本地仓库中的文档与平台文集内文档名字的差异``` #### ```可通过指定本地文档仓库中的地址来更新网站的文档``` #### ```可直接通过选项-a来自动查找本地与远程有差异的文档进行更新``` #### ```由于没有delete的api所以不能同步进行delete操作``` #### ```目前此脚本只适用于windows``` #### ```解决了打包的时候本地上传到平台上字符集乱码、更新旧文档麻烦、上传新文档麻烦的问题一行命令就可以完成想要的操作``` # 交流群 #### ```QQ群:766160484``` #### [点击此处入群](https://qm.qq.com/cgi-bin/qm/qr?k=KyexzZye9-DErkWvOjDRf8VADOhk5BMz&authKey=XOqIlTUqF2yjBC1lHmsxhpg4t/jA2OwTrUDz0mh8GJQ+4hP+kQHDleGz7hzDm6HJ&noverify=0) # 更新内容 ```timeline # V0.2.3 2022-10-27 修复了在-a,--updatelall指令执行时文集不存在导致的报错问题 ============= # V0.2.2 2022-9-8 发布了一个Pyinstaller打包的exe版本 将配置信息移到json文件了 ============= # V0.2.1 2022-8-28 修复了文件名特殊字符导致的找不到文件、抛异常 建议更新 ============= # V0.2.0 2022-8-27 新增两条选项-a --updateall 解决:修改文档内容较多的时候一个一个上传较麻烦的问题 -a选项:拿本地所有文档内容和远程文档内容作差异比较 将有差异的文档更新 --updateall选项:指定一个仓库的子目录(二级目录) 在此目录下的文档和远程文档做差异比较将有差异的文档更新 优化代码逻辑,修复文件名有若干空格导致的报错和重复上传问题 ============= # V0.1.2 2022-8-25 新增一条命令选项-s 解决:本地文档改名后与网站文档名不一致导致的更新失败 新增自动创建文集功能 解决:本地新建 文集(子文件夹) 后网站文集未更新的问题 将原有的复杂命令现在改为选项 修复有文档在本地仓库主目录时的报错问题(现在不允许有文档在主目录了) 在push之前会检查本地文集与网站文集是否都存在,不存在则会先创建再进行文档检查 ============= # V0.1.1 2022-8-24 新增文档更新功能 命令:doctools --update ${文档路径} ============= # V0.1.0 2022-8-23 开始编写 支持一键push 命令:doctools --push ========== ``` # 一、如何使用? ### 1.通过py源码 或 直接下载打包好的exe版本 > i [Windows安装Python教程](https://blog.csdn.net/weixin_46713695/article/details/125566730) > i [Python Windows installer 64位安装包](https://www.python.org/ftp/python/3.10.6/python-3.10.6-amd64.exe) ### 2.从[码云](https://gitee.com/atyin/mrdocTools)发行版下载(您可以点击下方[源码压缩包](https://www.yazyyzs.xyz/media/attachment/2022/09/MrDocTools.zip) 或 [打包版](https://gitee.com/atyin/mrdocTools/releases/download/v0.2.2/MrDocToolsExe.zip) 直接下载) > s **[mrdocTools源码](https://www.yazyyzs.xyz/media/attachment/2022/09/MrDocTools.zip)** > s **[mrdoctools打包版](https://gitee.com/atyin/mrdocTools/releases/download/v0.2.2/MrDocToolsExe.zip)** #### 2.1使用源码部署(需要py环境) - **1.下载MrDocTools.zip解压后存放在单独的文件夹内** - **2.将此文件夹添加到系统环境变量 名字:DOCTOOLS_HOME 再将 %DOCTOOLS_HOME% 添加到path** - **3.编辑config.json文件 配置指定内容详情查看2.3** #### 2.2使用exe版部署无需py环境(推荐此方法) - **1.下载MrDocToolsExe.zip解压后存放在单独的文件夹内** - **2.将此文件夹添加到系统环境变量 名字:DOCTOOLS_HOME 再将 %DOCTOOLS_HOME% 添加到path** - **3.编辑config.json文件 配置指定内容详情查看2.3** #### 2.3配置文件说明 ##### **base_url:网站地址** ##### **token:用户token** ##### **file_path:本地文档存放路径** ##### **我的配置:** ​ ![image-20220908231621642](https://www.yazyyzs.xyz/media/202209/image-20220908231621642_1662650208.png)) > > d **现在你就可以打开cmd输入 doctools -h会有以下输出信息** >> > > ![image-20220908231901321](https://www.yazyyzs.xyz/media/202209/image-20220908231901321_1662650346.png) > w **然后请确认您的本地文档仓库存放目录是否是以下结构** > d **注意! 子目录名称就是网站上对应的文集名称** > d **注意! 存放图片视频等文件的文件夹务必命名成src 否则脚本会当成文档上传导致报错!** ```mindmap # 主目录 - 子目录1 - 分目录1 - 分目录2 - 分目录n... - 子目录2 - 分目录1 - 分目录n... - 子目录3 - 分目录n... - 子目录n... ``` > w 我的目录结构: ```mindmap # F:\.md\mrdoc - F:\.md\mrdoc\Java高级知识笔记 - F:\.md\mrdoc\Java高级知识笔记\Java常用类 - F:\.md\mrdoc\Java高级知识笔记\多线程 - 目录n...... - F:\.md\mrdoc\Java基础部分笔记 - F:\.md\mrdoc\Java基础部分笔记\总和 - F:\.md\mrdoc\Java基础部分笔记\接口 - F:\.md\mrdoc\部分自动化脚本共享 - 目录n...... ``` > d 目录需要有三层结构 主目录-子目录-分目录 (可以有无数个上述目录,但是分目录下还有子目录就不获取了,并且仓库根目录不能存放文档) > d 当你完成了上述操作再将你的文档按照文件目录规范放进去,就可以使用脚本来push了 > s 在cmd中输入 ```doctools -p```如果push成功会有日志输出并且在网站刷新后会有本地仓库push上去的文档 # 二、命令大全 > i 命令: doctools > i -h | --help: 帮助 > i -p | --push: doctools -p > > > s 将本地文档仓库内的文档全部推送 > i -u | --update: doctools -u <要更新的文档本地文档仓库路径> > > > s 指定一个已存在网站上的文档 在本地仓库内的文档地址,更新至网站 > i -s | --updates: doctools -s <旧的文档名> <新文档的文档本地文档仓库路径> > > > s 如果你的新文档与网页上的文档文档名不一致会无法更新 此命令第一个参数是旧文档名字 第二个参数是本地新文档地址 > i -a | --updateall: doctools -a > > > s 本地文档与远程文档进行差异比较,将有差异的文档更新 > > > > s doctools --updateall > > > > s 指定一个本地文档仓库的子目录(二级目录)与远程文档进行差异比较,将有差异的文档更新 ### 有疑问可以加[交流群](https://qm.qq.com/cgi-bin/qm/qr?k=KyexzZye9-DErkWvOjDRf8VADOhk5BMz&authKey=XOqIlTUqF2yjBC1lHmsxhpg4t/jA2OwTrUDz0mh8GJQ+4hP+kQHDleGz7hzDm6HJ&noverify=0):766160484 # 三、代码部分 ```python # coding:utf-8 import getopt import json import sys import requests import os global base_url # 网站地址 global token # 用户token 在个人中心处获得 global file_path # 设置本地文档仓库位置 ExistsEocCount = 0 SonFolder = '' folder = '' tFileCopy = [] address = [] allAddress = [] idCount = [] wrongFile = [] cmdin = sys.argv # 获取文档标题(暂时弃用) def getFileTiitle(f1): with open(f1, encoding='utf-8') as f: try: firstLine = f.readlines()[0].rstrip() except: firstLine = '# TEST' return firstLine # 过滤掉地址和后缀返回文档名称 def getUrlFileName(f): return os.path.basename(f).split('.')[0] # 获取文档内容 def getFile(f): filen = '' try: with open(f, encoding='utf-8') as file_obj: for line in file_obj: filen += line return filen except FileNotFoundError: print('路径"{}"错误'.format(f)) sys.exit(0) # 子目录下文档查找 def addIsFile(docfile, tl, name, dir, id, existsEocCount): count = 0 for docFileData in docfile['data']: if tl.split('.')[0].replace(" ", "") == docFileData['name']: count += 1 existsEocCount += 1 continue if count == 0: allAddress.append(dir + '\\' + name + "\\" + tl) idCount.append(id) return existsEocCount # 获取指定Url文档夹下的所有文档 def getFileList(u): result = [f for f in os.listdir(u) if os.path.isfile(os.path.join(u, f))] return result # 获取当前token用户现有文集 def getAnthology(): url = base_url + '/api/get_projects/?token={}'.format(token) resp = requests.get(url=url) return resp.json() # 获取网站文档的所有集合 def getDocList(pid): url = base_url + '/api/get_docs/?token={}'.format(token) data = { 'pid': pid, 'sort': 0 } resp = requests.get(url=url, params=data) return resp.json() def getDoc(did): url = base_url + '/api/get_doc/?token={}'.format(token) data = { 'did': did, } resp = requests.get(url=url, params=data) return resp.json() # 推送新的文档 def pushDoc(pid, title, doc): url = base_url + '/api/create_doc/?token={}'.format(token) data = { 'pid': pid, 'title': title.strip('#').rstrip().replace(" ", ""), 'doc': doc } resp = requests.post(url=url, data=data) return resp.json() # 更新文档 def setUpdate(pid, did, title, doc): url = base_url + '/api/modify_doc/?token={}'.format(token) data = { 'pid': pid, 'did': did, 'title': title.strip('#').rstrip(), 'doc': doc } resp = requests.post(url=url, data=data) return resp.json() # 创建文集 def createAnthology(name, desc): url = base_url + '/api/create_project/?token={}'.format(token) data = { 'name': name, 'desc': desc, 'role': 0 } resp = requests.post(url=url, data=data) return resp.json() # 检测文集是否创建 如果没有会自动创建 def testingAnthology(): global tmp anthology = getAnthology()['data'] localFolder = os.listdir(file_path) for i in localFolder: if os.path.isfile(file_path + "\\" + i): value = '仓库根目录下不允许存在文档!此文档目录无法有效获取!请将仓库根目录下文档移动至子目录!' print(f"\033[0;31m{value}\033[0m") print("仓库根目录下:" + file_path + " 有文档:" + i) print("请将文档放置在子目录下") print("地址:" + file_path + "\\" + i) sys.exit() tmp = False for j in anthology: if j['name'] == i: tmp = True break else: tmp = False if not tmp: status = createAnthology(i, '系统自动创建,请及时修改简介')['status'] if status: print('已创建文集: "{}"'.format(i)) def push(news): # 获取文集 if news: print("获取远程文集中") anthology = getAnthology() anthologyPidNameJson = {} anthologyNamePidJson = {} for data in anthology['data']: anthologyNamePidJson[data['name']] = data['id'] anthologyPidNameJson[data['id']] = data['name'] # 获取本地仓库文集列表 if news: print("获取本地文集中") localAnyhologyNameJson = {} localDocNameLink = {} dir = os.listdir(file_path) for name in dir: link = file_path + '\\' + name if not os.path.isfile(link): dir1 = os.listdir(link) for name2 in dir1: if os.path.isfile(link + '\\' + name2) and name2.split('.')[1] == 'md': localAnyhologyNameJson[name2.split('.')[0]] = name localDocNameLink[name2.split('.')[0]] = link + '\\' + name2 continue else: dir2 = os.listdir(link + '\\' + name2) for name3 in dir2: if os.path.isfile(link + '\\' + name2 + '\\' + name3) and name3.split('.')[1] == 'md': localAnyhologyNameJson[''.join(name3.split()).split('.')[0]] = name localDocNameLink[''.join(name3.split()).split('.')[0]] = link + '\\' + name2 + '\\' + name3 # 获取远程文档和文集列表 if news: print("正在对比") anyhologyNameJson = {} for data in anthologyPidNameJson: docJson = getDocList(data) for data1 in docJson['data']: anyhologyNameJson[''.join(data1['name'].split())] = anthologyPidNameJson[data] addJson = {} for name in localAnyhologyNameJson: if name not in anyhologyNameJson: addJson[name] = anthologyNamePidJson[localAnyhologyNameJson[name]] docCount = len(addJson) print("本地共有:{}篇文档 {}篇文档待推送".format(len(localDocNameLink), docCount)) if docCount > 0: okCOunt = 0 errCount = 0 for data in addJson: print("正在推送:{}---{}".format(addJson[data], data)) status = pushDoc(addJson[data], getUrlFileName(localDocNameLink[data]), getFile(localDocNameLink[data])) if status['status']: okCOunt += 1 else: print("{}---{}推送失败 原因:{}".format(addJson[data], data, status)) print("共:{}篇 成功:{} 失败{}".format(len(addJson), okCOunt, errCount)) return True else: return False # 更新文档 def update(arg): folder = arg.split('\\') home = file_path.split('\\') if len(folder) > len(folder): anthologyName = folder[len(folder)] else: anthologyName = folder[len(home)] folder2 = "".join(os.path.basename(arg).split()) data = getAnthology()['data'] pid = 0 did = 0 for i in data: if i['name'] == anthologyName: pid = i['id'] data = getDocList(pid)['data'] for j in data: if j['name'] + '.md' in folder2: did = j['id'] result = setUpdate(pid, did, "".join(getUrlFileName(arg).split()), getFile(arg)) if result['status']: print('更新文档: "{}" 成功'.format("".join(getUrlFileName(arg).split()))) else: print('更新文档: "{}" 失败'.format("".join(getUrlFileName(arg).split())) + '-原因: {}'.format(result)) # 更新所有本地与远程有差异的文档 def updateAll(dir): print("正在进行更新前push操作") if not push(False): print("没有可上传文档") print("==================================") print("正在获取本地仓库中的文档") if len(dir) > 0: localDocLinkList = [] dirList = os.listdir(dir) for data in dirList: if os.path.isfile(dir + '\\' + data) and data.split('.')[1] == 'md': localDocLinkList.append(dir + '\\' + data) continue dirList1 = os.listdir(dir + '\\' + data) for data1 in dirList1: if os.path.isfile(dir + '\\' + data + '\\' + data1) and data1.split('.')[1] == 'md': localDocLinkList.append(dir + '\\' + data + '\\' + data1) else: firstLevelDir = os.listdir(file_path) dirList = [] localDocLinkList = [] # 获取本地文档仓库中的文集列表 for dir in firstLevelDir: if os.path.isfile(file_path + '\\' + dir): continue dirList.append(dir) # 从本地文集列表中拿到所有文档的地址 for docName in dirList: secondLevelDir = os.listdir(file_path + '\\' + docName) for docFileLink in secondLevelDir: if os.path.isfile(file_path + '\\' + docName + '\\' + docFileLink) and docFileLink.split(".")[ 1] == 'md': localDocLinkList.append(file_path + '\\' + docName + '\\' + docFileLink) continue threeLevelDir = os.listdir(file_path + '\\' + docName + '\\' + docFileLink) for docFileLink1 in threeLevelDir: if os.path.isfile(file_path + '\\' + docName + '\\' + docFileLink + '\\' + docFileLink1) and \ docFileLink1.split('.')[1] == 'md': localDocLinkList.append(file_path + '\\' + docName + '\\' + docFileLink + '\\' + docFileLink1) cloudDidJson = {} # 将本地的文档名提取出来 docNameList = [] for name in localDocLinkList: tlist = name.split('\\') tlist = tlist[len(tlist) - 1].split('.') docNameList.append(tlist[0]) # 吧文档名和对应的路径转换为json数据 docNameJson = {} i = 0 for add in docNameList: docNameJson[add] = localDocLinkList[i] i += 1 # 通过传统的update方法更新 # for link in docNameJson: # update(docNameJson[link]) # 过滤去重文集name列表 newList = [] for name in localDocLinkList: folder = name.split('\\') home = file_path.split('\\') if len(folder) > len(folder): anthologyName = folder[len(folder)] else: anthologyName = folder[len(home)] newList.append(anthologyName) newList = list(set(newList)) print("正在获取远程文档信息") # 远程去获取文集id anthology = getAnthology() for data in anthology['data']: for name1 in newList: if data['name'] == name1: cloudDidJson[name1] = data['id'] break # 拿一下远程所有文档的信息保存为json数据 docJson = {} for wName in cloudDidJson: docJson[wName] = getDocList((cloudDidJson.get(wName))) print("正在获取本地文档内容") localdocNameDataJson = {} for localDocName in docNameJson: localdocNameDataJson[localDocName] = getFile(docNameJson[localDocName]) print("差异比较中......") # 通过拿到的文档信息再拿一下文档的id和文档内容 docNameDidJson = {} # docName : Did docNamePidJson = {} # docName : Pid anthologyPidNameJson = {} docNameDataJson = {} for data in docJson: for data1 in docJson[data]['data']: docNameDidJson["".join(data1['name'].split())] = data1['id'] docNamePidJson["".join(data1['name'].split())] = cloudDidJson[data] anthologyPidNameJson[cloudDidJson[data]] = data docData = getDoc(data1['id'])['data'] docNameDataJson[''.join(docData['name'].split())] = docData['md_content'] updateDocNameJson = {} for docName in docNameJson: try: if localdocNameDataJson[docName] != docNameDataJson[''.join(docName.split())]: updateDocNameJson[docName] = docNameJson[docName] except KeyError: print('文档名有特殊字符或空格 请修改后上传 文档名=”{}“ '.format(docName)) print("本地共有文档{} 有差异的文档:{}".format(len(localdocNameDataJson), len(updateDocNameJson))) if len(updateDocNameJson) > 0: okCount = 0 ErrCount = 0 for j in updateDocNameJson: pid = docNamePidJson["".join(j.split())] did = docNameDidJson["".join(j.split())] docName = getUrlFileName(updateDocNameJson[j]) print("推送 '{}' : '{}' 中".format(anthologyPidNameJson[pid], j)) result = setUpdate(pid, did, docName, getFile(updateDocNameJson[j])) if result['status']: okCount += 1 else: ErrCount += 1 print("文集 '{}' : '{}' 更新失败,原因{}".format(anthologyPidNameJson[pid], docName, result)) print("==================================") print("推送完成 共:{}个差异 成功:{}个 失败:{}个".format(len(updateDocNameJson), okCount, ErrCount)) # 旧文档名字和新文档不一致的更新方法 def ifNameUpdate(oldName, newAddress): essayName = newAddress[0].split('\\') home = file_path.split('\\') if len(folder) > len(essayName): anthologyName = essayName[len(essayName)] else: anthologyName = essayName[len(home)] try: if oldName.split('.')[1] == 'md': oldName = oldName.split('.')[0] except Exception: pass data = getAnthology()['data'] pid = 0 did = 0 for i in data: if i['name'] == anthologyName: pid = i['id'] data = getDocList(pid)['data'] for j in data: if j['name'] in oldName: did = j['id'] tmpi = len(newAddress[0].split("\\")) result = setUpdate(pid, did, getUrlFileName(newAddress[0].split("\\")[tmpi - 1]), getFile(newAddress[0])) if result['status']: print('更新文档: 旧:"{}" ---->>'.format(oldName) + '新: "{}" 成功!'.format(newAddress[0].split("\\")[tmpi - 1])) else: print('更新文档: "{}" 失败'.format(oldName) + '-原因: {}'.format(result)) print("可能是旧文档名不正确!") def main(argv): global args try: opts, args = getopt.getopt(argv[1:], "hpau:s:", ["help", "push", "update=", "updates=", "updateall="]) except Exception: opts = [('-h', '')] print("命令错误") for opt, arg in opts: if opt in ("-h", "--help"): print('-选项后带: --选项后带= 代表有参数') print('-h | --help: 帮助') print('-p | --push: doctools -p' ' \n 将本地文档仓库内的文档全部推送') print('-u: | --update=: doctools -u ' ' \n 指定一个本地仓库内的文档地址,更新至网站') print('-s: | --updates=: doctools -s ' ' \n 如果你的新文档与网页上的文档文档名不一致会无法更新 此命令第一个参数是旧文档名字 第二个参数是本地新文档地址') print('-a | --updateall=: doctools -a ' ' \n 本地文档与远程文档进行差异比较,将有差异的文档更新' ' \n : doctools --updateall ' ' \n 指定一个本地文档仓库的子目录(二级目录)与远程文档进行差异比较,将有差异的文档更新') sys.exit() elif opt in ("-p", "--push"): # 本地文件夹(文集)与平台文集做查找 没有就新增 testingAnthology() if not push(True): print('没有任何新的文档,如需获取帮助可以-h 或 --help') elif opt in ("-u", "--update"): try: update(arg) except IndexError: print("参数错误") os.system('doctools -h') elif opt in ("-s", "--updates"): ifNameUpdate(arg, args) elif opt in ("-a", "--updateall"): testingAnthology() updateAll(arg) else: print("没有{}此选项".format(opt)) if __name__ == "__main__": env = os.environ.get("DOCTOOLS_HOME") if env is None: print("请在系统内配置脚本所在路径的环境变量:DOCTOOLS_HOME") sys.exit() try: with open('{}/config.json'.format(env), 'r', encoding='utf8') as fp: json_data = json.load(fp) base_url = json_data['base_url'] token = json_data['token'] file_path = json_data['file_path'] except KeyError: print("json config file key is not exists") sys.exit(0) except FileNotFoundError: print("json config file is not exists") sys.exit(0) except: print("配置文件有误") sys.exit(0) if sys.argv[1] == '-h' or sys.argv[1] == '--help': main(sys.argv) elif len(file_path) == 0 or len(token) == 0 or len(base_url) == 0: print("首次使用脚本请在 {} 目录下的config.json内配置对应信息".format(env)) else: try: main(sys.argv) except KeyboardInterrupt: print("程序被手动终止!") sys.exit(0) except Exception as e: print('请检查您 的参数 如果有bug可将问题和报错信息复现发送至邮箱:2784404835@qq.com qq群:766160484') ```