问题
对于开发者,编写文档是非常头疼的问题。但是又是项目交付必须要的部分。
说明
-
此文是基于drf_yasg插件生成详细设计和概要设计文档,但只是框架性的,以及接口部分可以自动生成,个性化内容还需要自己去填充。
-
输出格式为markdown格式,还需要编辑后,转换为pdf格式,形成文档。
代码部分
# -*- coding: UTF-8 -*-
# Desc : 项目输出api文档(详细设计文档和概要设计)
import time
import requests
def magic_api(markdown_path: str, swagger_url: str):
"""
将api输出为markdown文档
:param markdown_path: markdown输出路径
:param swagger_url: swagger的json数据请求url
:return:
"""
res = requests.get(swagger_url)
swagger_info = res.json()
# title = swagger_info['info']['title']
# TODO 修改项目名称
project_name='项目名称'
title_txt = '# %s 详细设计文档' % project_name
base_url = swagger_info['basePath']
# 文档前部分
files_list=[]
files_list.append('- 文档时间:%s'%(time.strftime('%Y-%m-%d'),))
files_list.append('## 1 简介')
files_list.append('### 1.1 目的')
files_list.append(' 本文档旨在说明%s各模块的总体设计思路,为软件编程和系统维护提供依据。本说明书的预期读者为系统设计人员、软件开发人员、软件测试人员和项目评审人员。'%(project_name,))
files_list.append('### 1.2 范围')
# TODO 修改模块组成
files_list.append(' 对%s中的各个模块进行详细的描述,包括%s。'%(project_name,'模块组成'))
files_list.append('### 1.3 缩略语')
files_list.append("|缩略语|英文全称|中文解释|")
files_list.append("|:-:|:-:|:-:|")
files_list.append('|%s|' % '|'.join(['','','']))
files_list.append('## 2 系统架构')
files_list.append('## 3 功能结构')
files_list.append('## 4 各模块详细设计')
# 文档结尾部分
ends_list=[]
ends_list.append('## 5 数据流详细设计')
ends_list.append('### 5.1 数据源')
ends_list.append('### 5.2 数据流转')
ends_list.append('## 6 系统部署描述')
ends_list.append('## 7 参考资料')
# 文档api部分
words_list = []
model_tags={
# TODO
# 模块名:模块含义
'auth':'登录模块',
}
# 遍历url
for k, v in swagger_info['paths'].items():
url = base_url + k
# tags为模块名称占位符
files_text = [url, 'tags']
files_text.append("\n")
files_text.append("#### %s" % url)
for method, values in v.items():
if method == 'parameters':
continue
files_text.append("- **请求方法:%s**" % method)
files_text.append("接口用途:%s" % values['description'])
# 模块名称
files_text[1] = values['tags'][0]
# 获取请求参数
files_text.append('- 请求参数')
files_text.append("|参数名称|参数含义|数据类型|是否必须|枚举数据|默认值|备注|")
files_text.append("|-|-|:-:|:-:|:-:|:-:|-|")
# 处理参数
for parameter in values['parameters']:
parm_list = []
# 处理结构化(object类型)的参数
if parameter['name'] == 'data':
schema = parameter['schema']
for zi_parm, zi_value in schema['properties'].items():
schema_list = []
schema_list.append(zi_parm)
schema_list.append(zi_value['description'])
schema_list.append(zi_value['type'])
schema_list.append('')
schema_list.append('')
schema_list.append('')
schema_list.append('')
parm_list.append(schema_list)
# print('参数名称:', zi_parm, '参数描述:', zi_value['description'], '参数类型:', zi_value['type'])
else:
parm_list.append(parameter['name'])
parm_list.append(parameter['description'])
parm_list.append(parameter['type'])
parm_list.append('是' if parameter.get('required') else '否')
parm_list.append(str(parameter.get('enum')) if parameter.get('enum') else '')
parm_list.append(str(parameter.get('default')) if parameter.get('default') else '')
parm_list.append('')
for i in parm_list:
if isinstance(i, list):
files_text.append('|%s|' % '|'.join(i))
if parm_list and parm_list[0]:
if isinstance(parm_list[0], str):
files_text.append('|%s|' % '|'.join(parm_list))
# 响应
files_text.append('- 响应结果')
files_text.append("|响应状态码|描述|返回值示例|备注|")
files_text.append("|:-:|:-:|:-:|:-:|")
for res_key, res_value in values['responses'].items():
resp_list = []
resp_list.append(res_key)
resp_list.append(res_value['description'])
examples = res_value.get('examples')
example_txt = ''
if examples:
for k, v in examples.items():
example_txt += k + ' : '
example_txt += str(v)
resp_list.append(example_txt)
resp_list.append('')
files_text.append('|%s|' % '|'.join(resp_list))
words_list.append(files_text)
words_list.sort(key=lambda x: x[1])
with open(markdown_path, 'w', encoding='utf-8') as fw:
fw.write(title_txt + '\n\n')
fw.write('\n'.join(files_list)+'\n')
model_name = []
for url in words_list:
if url[1] not in model_name:
chinese_model_name=model_tags.get(url[1])
if not chinese_model_name:
chinese_model_name=url[1]
fw.write('### %s%s' % (chinese_model_name, '\n'))
model_name.append(url[1])
fw.write('\n'.join(url[2:]) + '\n')
fw.write('\n'.join(ends_list))
def outline_design(markdown_path: str, swagger_url: str):
"""
将api输出为markdown文档,作为概要设计
:param markdown_path: markdown输出路径
:param swagger_url: swagger的json数据请求url
:return:
"""
res = requests.get(swagger_url)
swagger_info = res.json()
# title = swagger_info['info']['title']
project_name = '项目名称'
title_txt = '# %s 概要设计文档' % project_name
base_url = swagger_info['basePath']
# 文档前部分
files_list = []
files_list.append('- 文档时间:%s' % (time.strftime('%Y-%m-%d'),))
files_list.append('## 1 简介')
files_list.append('### 1.1 文档目标')
files_list.append('1. 本文档旨在说明%s系统的总体设计思路,为软件编程和系统维护提供依据。'%(project_name,))
files_list.append('2. 本文描述%s系统结构,给出模块的边界。'%(project_name,))
files_list.append('3. 此文档从构架方面对%s系统进行综合概述,记录并表述已在构架方面对目标系统作出的重要决定。'%(project_name,))
files_list.append('4. 此文档针对%s的需求文档进行业务架构建模,并对后续的详细设计和Coding提供指导和参照。'%(project_name,))
files_list.append('### 1.2 适用对象')
files_list.append('本文适合相关的产品、研发、测试人员等相关人员阅读。')
files_list.append('1. 产品经理:了解此系统的功能架构和模块设计。')
files_list.append('2. 开发工程师:遵循本文档进行详细设计和后续的编码工作')
files_list.append('3. 测试人员:了解此系统的功能架构和模块设计,指导测试用例编写。')
files_list.append('### 1.3 范围')
files_list.append("本文档主要涉及:")
files_list.append("1. 根据产品需求及原型设计进行功能模块划分,本系统模块主要包括大屏首页,遥感底图,图斑模块,用户模块,报表模块。")
files_list.append("2. 对系统的各个模块进行详细的描述,包括各个功能模块间的关系描述,以及模块对外提供接口的详细描述。")
files_list.append("本文档不涉及以下内容:")
files_list.append("1. 不涉及各个模块内部详细功能,此部分请参考《%s系统详细设计》。"%(project_name,))
files_list.append('### 1.4 参考资料')
files_list.append('### 1.5 术语列表')
files_list.append("|术语(缩写)|解释|")
files_list.append("|:-:|:-:|")
files_list.append('|%s|' % '|'.join(['','']))
files_list.append('## 2. 总体技术方案')
files_list.append('### 2.1 设计概述')
files_list.append('1. 系统从功能实现的角度主要分为')
files_list.append('2. 系统从页面展示角度主要分为')
files_list.append('### 2.2 总体架构')
files_list.append('#### 2.2.1 软件结构图')
files_list.append('#### 2.2.2 功能结构图')
files_list.append('#### 2.2.3 接口说明')
# 文档结尾部分
ends_list = []
ends_list.append('#### 2.2.4 数据流分析')
ends_list.append('##### 2.2.4.1 数据源')
ends_list.append('##### 2.2.4.2 数据流转')
ends_list.append('数据的处理分为三个阶段:数据获取、数据处理、数据入库。')
ends_list.append('(1)数据获取')
ends_list.append('(2)数据处理')
ends_list.append('(3)数据及入库')
ends_list.append('##### 2.2.4.3 流程图')
# 文档api部分
words_list = []
model_tags = {
# TODO
'auth': '登录模块',
}
# 遍历url
for k, v in swagger_info['paths'].items():
url = base_url + k
# tags为模块名称占位符
files_text = [url, 'tags']
files_text.append("\n")
files_text.append("###### %s" % url)
for method, values in v.items():
if method == 'parameters':
continue
files_text.append("- **请求方法:%s**" % method)
files_text.append("接口用途:%s" % values['description'])
# 模块名称
files_text[1] = values['tags'][0]
# 获取请求参数
files_text.append('- 请求参数')
files_text.append("|参数名称|参数含义|数据类型|是否必须|枚举数据|默认值|备注|")
files_text.append("|-|-|:-:|:-:|:-:|:-:|-|")
# 处理参数
for parameter in values['parameters']:
parm_list = []
# 处理结构化(object类型)的参数
if parameter['name'] == 'data':
schema = parameter['schema']
for zi_parm, zi_value in schema['properties'].items():
schema_list = []
schema_list.append(zi_parm)
schema_list.append(zi_value['description'])
schema_list.append(zi_value['type'])
schema_list.append('')
schema_list.append('')
schema_list.append('')
schema_list.append('')
parm_list.append(schema_list)
# print('参数名称:', zi_parm, '参数描述:', zi_value['description'], '参数类型:', zi_value['type'])
else:
parm_list.append(parameter['name'])
parm_list.append(parameter['description'])
parm_list.append(parameter['type'])
parm_list.append('是' if parameter.get('required') else '否')
parm_list.append(str(parameter.get('enum')) if parameter.get('enum') else '')
parm_list.append(str(parameter.get('default')) if parameter.get('default') else '')
parm_list.append('')
for i in parm_list:
if isinstance(i, list):
files_text.append('|%s|' % '|'.join(i))
if parm_list and parm_list[0]:
if isinstance(parm_list[0], str):
files_text.append('|%s|' % '|'.join(parm_list))
# 响应
files_text.append('- 响应结果')
files_text.append("|响应状态码|描述|返回值示例|备注|")
files_text.append("|:-:|:-:|:-:|:-:|")
for res_key, res_value in values['responses'].items():
resp_list = []
resp_list.append(res_key)
resp_list.append(res_value['description'])
examples = res_value.get('examples')
example_txt = ''
if examples:
for k, v in examples.items():
example_txt += k + ' : '
example_txt += str(v)
resp_list.append(example_txt)
resp_list.append('')
files_text.append('|%s|' % '|'.join(resp_list))
words_list.append(files_text)
words_list.sort(key=lambda x: x[1])
with open(markdown_path, 'w', encoding='utf-8') as fw:
fw.write(title_txt + '\n\n')
fw.write('\n'.join(files_list) + '\n')
model_name = []
for url in words_list:
if url[1] not in model_name:
chinese_model_name = model_tags.get(url[1])
if not chinese_model_name:
chinese_model_name = url[1]
fw.write('##### %s%s' % (chinese_model_name, '\n'))
model_name.append(url[1])
fw.write('\n'.join(url[2:]) + '\n')
fw.write('\n'.join(ends_list))
if __name__ == '__main__':
# drf_yasg接口url
url = 'http://172.27.11.200:8001/redoc/?format=openapi'
# 详细设计文档
# magic_api('./test_api.md', url)
# 概要设计文档
outline_design('./outline_design.md', url)