From 5967b1aeed5e8686c586e4dd8df2df5f7170a487 Mon Sep 17 00:00:00 2001 From: han0 Date: Wed, 22 Feb 2023 15:31:12 +0800 Subject: [PATCH] feat: --- nc_http/core/documents/__init__.py | 46 ++++++++++++++ nc_http/core/documents/api/__init__.py | 9 +++ nc_http/core/documents/api/builder.py | 18 ++++++ nc_http/core/documents/api/creator.py | 76 +++++++++++++++++++++++ nc_http/core/documents/api/template.docx | Bin 0 -> 13075 bytes 5 files changed, 149 insertions(+) create mode 100644 nc_http/core/documents/__init__.py create mode 100644 nc_http/core/documents/api/__init__.py create mode 100644 nc_http/core/documents/api/builder.py create mode 100644 nc_http/core/documents/api/creator.py create mode 100644 nc_http/core/documents/api/template.docx diff --git a/nc_http/core/documents/__init__.py b/nc_http/core/documents/__init__.py new file mode 100644 index 0000000..3867fc7 --- /dev/null +++ b/nc_http/core/documents/__init__.py @@ -0,0 +1,46 @@ +# 重构内容: +# 1.文档文件通过 docxtpl 模版文件生成 +# 2.数据库文档数据来源为数据库系统信息查询 +# Oracle +# Mysql +# 3.API 文档数据来源 swagger json 接口 + + +class DocumentCreator: + has_catalog = True + + def __init__(self, *args, **kwargs): + pass + + def create_cover(self): + pass + + def init_catalog(self): + pass + + def create_catalog(self): + pass + + def create_content(self): + pass + + def create(self): + self.create_cover() + self.init_catalog() + self.create_content() + self.create_catalog() + + +class DocDataBuilder: + def __init__(self, *args, **kwargs): + self._data = None + + def generate(self): + yield + + def clean(self, item): + return item + + @property + def data(self): + return self._data diff --git a/nc_http/core/documents/api/__init__.py b/nc_http/core/documents/api/__init__.py new file mode 100644 index 0000000..8214029 --- /dev/null +++ b/nc_http/core/documents/api/__init__.py @@ -0,0 +1,9 @@ +from nc_http.core.documents.api.builder import ApiDocDataBuilder +from nc_http.core.documents.api.creator import ApiDocumentCreator + +if __name__ == '__main__': + _builder = ApiDocDataBuilder(swagger_json_url='http://localhost:7027/apispec_1.json') + creator = ApiDocumentCreator( + builder=_builder, filename='text.docx', meta={'number_prefix': '3.1.'}, template='./template.docx' + ) + creator.create() diff --git a/nc_http/core/documents/api/builder.py b/nc_http/core/documents/api/builder.py new file mode 100644 index 0000000..706023c --- /dev/null +++ b/nc_http/core/documents/api/builder.py @@ -0,0 +1,18 @@ +import json + +import requests + +from nc_http.core.documents import DocDataBuilder + + +class ApiDocDataBuilder(DocDataBuilder): + def __init__(self, swagger_json_url, *args, **kwargs): + self.swagger_json_url = swagger_json_url + super().__init__(*args, **kwargs) + + @property + def data(self): + if self._data is None: + data = json.loads(requests.get(self.swagger_json_url).content) + self._data = data + return self._data diff --git a/nc_http/core/documents/api/creator.py b/nc_http/core/documents/api/creator.py new file mode 100644 index 0000000..c8036f0 --- /dev/null +++ b/nc_http/core/documents/api/creator.py @@ -0,0 +1,76 @@ +from docxtpl import DocxTemplate + +from nc_http.core.documents import DocumentCreator + + +class ApiDocumentCreator(DocumentCreator): + definition_map = {} + + def __init__(self, builder, filename, meta, template=None, **kwargs): + self.builder = builder + self.filename = filename + self.meta = meta + self.template = template or r'./trash/documents/api/template.docx' + self.api_description = None + super().__init__(**kwargs) + + def parse_api(self): + api = self.builder.data + + for definition_name, definition in api['definitions'].items(): + # print(definition) + definition_content = {} + if definition.get('type') == 'object': + definition_content = definition['properties'] + elif definition.get('type') == 'array': + definition_content = definition['items'].get('properties', {}) + + # print(definition_content) + self.definition_map[definition_name] = definition_content + + api_description = {} + for path, methods in api['paths'].items(): + ''' + path_content: { + 'get': { + 'parameters': [ + { + 'default': '/u255.jpeg', + 'description': '文件 key', + 'in': 'path', + 'name': 'key', + 'required': True, + 'type': 'string' + } + ], + 'responses': { + '200': {'description': '文件','schema': {'type': 'file'}} + }, + 'security': [{'Bearer': []}], + 'summary': '获取文件', + 'tags': ['文件'] + } + } + ''' + for method, content in methods.items(): + tag = content['tags'][0] + content['path'] = path + content['method'] = method + api_description.setdefault(tag, []) + api_description[tag].append(content) + + self.api_description = api_description + + def create_content(self): + self.parse_api() + + tpl = DocxTemplate(self.template) + + context = { + 'api_description': self.api_description, + 'definition_map': self.definition_map, + 'table_fields': ['参数名', '必选', '类型', '位置', '说明'], + } + + tpl.render(context) + tpl.save('text.docx') diff --git a/nc_http/core/documents/api/template.docx b/nc_http/core/documents/api/template.docx new file mode 100644 index 0000000000000000000000000000000000000000..2e8d117515146febdfd2876dbcb6605deb613083 GIT binary patch literal 13075 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 literal 0 HcmV?d00001