diff --git a/Desktop/pecan-swagger/.gitignore b/Desktop/pecan-swagger/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6d7b9c17f7d3c0a133b2328a1c52005d3e39f75d
--- /dev/null
+++ b/Desktop/pecan-swagger/.gitignore
@@ -0,0 +1,57 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# Build
+example_wsme_app.json
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
diff --git a/Desktop/pecan-swagger/.idea/.gitignore b/Desktop/pecan-swagger/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..619a01448f8f76b68316eb7448b251048f928fbe
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/../../../../../../:\Users\fengdelu\Desktop\pecan-swagger-master\.idea/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/Desktop/pecan-swagger/.idea/.name b/Desktop/pecan-swagger/.idea/.name
new file mode 100644
index 0000000000000000000000000000000000000000..8fdfb52898ee29de2f11abd73cb805aac01cf3cd
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/.name
@@ -0,0 +1 @@
+model.py
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/.idea/inspectionProfiles/profiles_settings.xml b/Desktop/pecan-swagger/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/.idea/misc.xml b/Desktop/pecan-swagger/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1f9f903a0ae222d0b09fefd4115838371482fffd
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/.idea/modules.xml b/Desktop/pecan-swagger/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..313b4d9e4bcb9d88ab84a5495d1540ea99290ac1
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/.idea/pecan-swagger-master.iml b/Desktop/pecan-swagger/.idea/pecan-swagger-master.iml
new file mode 100644
index 0000000000000000000000000000000000000000..8b8c395472a5a6b3598af42086e590417ace9933
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/pecan-swagger-master.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/.idea/vcs.xml b/Desktop/pecan-swagger/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8bd613e88568d158c1cc21515d3ab09ae9dea137
--- /dev/null
+++ b/Desktop/pecan-swagger/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/LICENSE b/Desktop/pecan-swagger/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..c1c86081e1b038d640bbb719c6dc94648c45b213
--- /dev/null
+++ b/Desktop/pecan-swagger/LICENSE
@@ -0,0 +1,57 @@
+Copyright (c) 2015, michael mccune
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of pecan-swagger nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Includes some source code from the Pecan project, http://www.pecanpy.org/
+
+Copyright (c) <2011>, Jonathan LaCour
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of the nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Desktop/pecan-swagger/README.md b/Desktop/pecan-swagger/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..cdff1a12d34b3c71485f9e02105f50f3ec6b3346
--- /dev/null
+++ b/Desktop/pecan-swagger/README.md
@@ -0,0 +1,28 @@
+<<<<<<< HEAD
+fix:tox error
+
+1.remove unused function library
+2.fix line too long
+3.fix blank lines
+
+=======
+# Pecan Swagger
+
+## 1. 项目背景
+
+Pecan-Swagger 用于为 Pecan Web 服务生成 Swagger 文档。该项目源自 ,目前原 repo 已无人维护,[原示例链接](examples/README.rst)。
+
+Pecan 是 OpenStack 诸多模块中比较受欢迎的一种 Restful API 框架,新兴的项目大多采用 Pecan,比如 Magnum / CloudKitty 等等。Pecan-Swagger 依然是有很大价值的,因此我们再这个 repo 继续更新 Pecan-Swagger。
+
+## 2. 需要预先掌握的技能
+
+1. [Python 三日课程](https://gitee.com/wu-wen-xiang/training-python/blob/master/doc/learning-python-in-3-days.md)
+2. [Git 基本使用](https://gitee.com/wu-wen-xiang/training-python/blob/master/doc/learning-python-in-3-days.md#31-%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6)
+3. [Markdown 基本语法](https://markdown.com.cn/basic-syntax/)
+4. [Gitee 合作](https://gitee.com/help/articles/4346)
+5. [Swagger](https://editor.swagger.io/)
+
+## 3. 预期目标
+
+1. 对接 Pecan 最近的 Stable 版本,提供 Pecan-Swagger 模块,为 Pecan 项目提供在线的 Swagger 文档。
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
diff --git a/Desktop/pecan-swagger/README.rst b/Desktop/pecan-swagger/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1bc6070dd2d9c0851f07aedf2b0cb1b8a7a9966c
--- /dev/null
+++ b/Desktop/pecan-swagger/README.rst
@@ -0,0 +1,66 @@
+=====================
+Pecan Swagger Project
+=====================
+
+some helpers to create swagger output from a pecan app
+
+example usage
+-------------
+
+given a file named ``myapp.py``
+
+::
+
+ import pecan
+ from pecan_swagger import decorators as swagger
+
+
+ @swagger.path('profile', 'Profile', 'Root')
+ class ProfileController(object):
+
+ @pecan.expose(generic=True, template='index.html')
+ def index(self):
+ return dict()
+
+ @index.when(method='POST')
+ def index_post(self, **kw):
+ print(kw)
+ pecan.redirect('/profile')
+
+
+ @swagger.path('/', 'Root')
+ class RootController(object):
+
+ profile = ProfileController()
+
+and another file named ``myapp-doc.py``
+
+::
+
+ import pprint
+
+ from pecan_swagger import utils
+ import myapp
+
+ pp = pprint.PrettyPrinter(indent=2)
+ pp.pprint(utils.swagger_build('myapp', '1.0'))
+
+
+the following will be produced when run
+
+::
+
+ $ python myapp-doc.py
+ {
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0",
+ "title": "myapp"
+ },
+ "paths": {
+ "/profile": {
+ "POST": {},
+ "GET": {}
+ }
+ }
+ }
diff --git a/Desktop/pecan-swagger/examples/.gitignore b/Desktop/pecan-swagger/examples/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/README.rst b/Desktop/pecan-swagger/examples/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/__init__.py b/Desktop/pecan-swagger/examples/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.dockerignore b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..ecf5595b42bfed44860023b32dda892f3fae9b54
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.dockerignore
@@ -0,0 +1,10 @@
+.DS_Store
+.dockerignore
+.gitignore
+.drone.yml
+README.md
+**/*.pyc
+**/.venv
+**/__pycache__
+**/industrialai.egg-info
+**/build
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.drone.yml b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.drone.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6b329287c7901d822ac9ca9394c7944a24da4546
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.drone.yml
@@ -0,0 +1,70 @@
+kind: pipeline
+type: exec
+name: push-build
+
+clone:
+ depth: 5
+
+platform:
+ os: linux
+ arch: amd64
+
+node:
+ node: ci-buildx-2
+
+steps:
+ - name: build-image
+ environment:
+ DOCKER_USER:
+ from_secret: DOCKER_USERNAME
+ DOCKER_PASS:
+ from_secret: DOCKER_PASSWORD
+ DOCKER_REGISTRY: docker.io
+ DOCKER_ORG: 99cloud
+ DOCKER_TAG: latest
+ commands:
+ - env
+ - mkdir -pv $HOMEPATH/.docker/buildx/instances
+ - cp /root/.docker/buildx/instances/cicd $HOMEPATH/.docker/buildx/instances/cicd
+ - docker buildx ls
+ - docker buildx use cicd
+ - echo -n $DOCKER_PASS | docker login $DOCKER_REGISTRY --username $DOCKER_USER --password-stdin
+ - docker buildx build -f Dockerfile --build-arg BRANCH=${DRONE_BRANCH} --build-arg REPO_URL=${DRONE_REPO} --build-arg COMMIT_REF=${DRONE_COMMIT_SHA} -t $DOCKER_REGISTRY/$DOCKER_ORG/industrial-ai-apiserver:$DOCKER_TAG --tag $DOCKER_REGISTRY/$DOCKER_ORG/industrial-ai-apiserver:$DOCKER_TAG-${DRONE_COMMIT_SHA:0:6} --platform=linux/arm64,linux/amd64 . --push
+ - name: clean
+ environment:
+ DOCKER_REGISTRY: docker.io
+ commands:
+ - docker logout $DOCKER_REGISTRY || true
+ when:
+ status:
+ - success
+ - failure
+ - name: predeploy
+ commands:
+ - cp -rf /root/.ssh ~/
+ - name: deploy-to-aliyun
+ commands:
+ - ssh maqiao-cd "docker stop industrial-ai-apiserver || true"
+ - ssh maqiao-cd "docker rm industrial-ai-apiserver || true"
+ - ssh maqiao-cd "docker rmi 99cloud/industrial-ai-apiserver:latest || true"
+ - ssh maqiao-cd "docker pull 99cloud/industrial-ai-apiserver:latest"
+ - ssh maqiao-cd "docker run -d -p 8888:8080 --name="industrial-ai-apiserver" -v ~/data:/data -v /etc/localtime:/etc/localtime 99cloud/industrial-ai-apiserver:latest || true"
+ - ssh maqiao-cd "sleep 10"
+ - ssh maqiao-cd "docker exec industrial-ai-apiserver python datainit.py"
+ - name: wechat on success
+ commands:
+ - bash -x wechat-robot-ai-success.sh ${DRONE_COMMIT} ${DRONE_COMMIT_BRANCH} "${DRONE_COMMIT_AUTHOR_NAME}" ${DRONE_REPO_OWNER} ${DRONE_REPO_NAME} ${DRONE_COMMIT_AUTHOR_EMAIL} ${DRONE_BUILD_NUMBER} ${DRONE_BUILD_STARTED}
+ when:
+ status:
+ - success
+ - name: wechat on failure
+ commands:
+ - bash -x wechat-robot-ai-failure.sh ${DRONE_COMMIT} ${DRONE_COMMIT_BRANCH} "${DRONE_COMMIT_AUTHOR_NAME}" ${DRONE_REPO_OWNER} ${DRONE_REPO_NAME} ${DRONE_COMMIT_AUTHOR_EMAIL} ${DRONE_BUILD_NUMBER} ${DRONE_BUILD_STARTED}
+ when:
+ status:
+ - failure
+trigger:
+ event:
+ - push
+ branch:
+ - master
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitignore b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a101926a6b19def0f29644b2a2ef79a2d397af04
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitignore
@@ -0,0 +1,72 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+cloudkitty.egg-info
+.idea/
+.python-version
+
+# Configuration file
+etc/cloudkitty/cloudkitty.conf.sample
+etc/cloudkitty/policy.yaml.sample
+
+# vscode
+.vscode
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.stestr/
+.coverage
+.cache
+coverage.xml
+cover
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+doc/build/
+
+# Rope
+.ropeproject/
+
+# Others
+*.sqlite
+*.swp
+*~
+*.swn
+*.swo
+*.DS_Store
+
+# tox -e docs
+AUTHORS
+ChangeLog
+
+releasenotes/build
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitreview b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitreview
new file mode 100644
index 0000000000000000000000000000000000000000..05d5ef73046af5d3c196dc297c0bcf04369fee8f
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=review.sh.99cloud.net
+port=29418
+project=fifth-element/industrial-ai-apiserver
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.stestr.conf b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.stestr.conf
new file mode 100644
index 0000000000000000000000000000000000000000..3c04f30858fe06aba6c390d0bd36c248cc8d8425
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/.stestr.conf
@@ -0,0 +1,4 @@
+[DEFAULT]
+test_path=./industrialai/tests
+top_dir=./
+group_regex=gabbi\.(suitemaker|driver)\.(test_[^_]+_[^_]+)
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/Dockerfile b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..68e04fabd829d8281c64e25bafe06378ad3edb0d
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/Dockerfile
@@ -0,0 +1,16 @@
+FROM docker.io/library/python:3.9.10-alpine
+
+LABEL purpose="Industrial AI API Server"
+
+RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
+RUN apk update && apk add sqlite && apk add build-base
+RUN mkdir -p /home/www/industrialai
+WORKDIR /home/www/industrialai
+COPY . /home/www/industrialai
+
+RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r /home/www/industrialai/requirements.txt
+RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gunicorn==20.0.0
+RUN pip install /home/www/industrialai
+
+ENTRYPOINT [ "gunicorn_pecan" ]
+CMD ["config.py"]
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/MANIFEST.in b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..c922f11ad7c4967578bf1ecaf226e64db9fe7982
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/MANIFEST.in
@@ -0,0 +1 @@
+recursive-include public *
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/README.md b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2e34627e118c138fd4ce375e853ce7d9bd00f69d
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/README.md
@@ -0,0 +1,61 @@
+# industrial-ai-apiserver
+
+数据字典 & API:
+
+## 本地调试
+
+```bash
+cd industrial-ai-apiserver
+
+# 创建 python 虚拟环境
+python -m virtualenv .venv
+
+# 进入 python 虚拟环境
+.venv\Scripts\activate
+
+# 安装依赖
+pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
+
+# 清理原数据库
+rm -rf /tmp/industrialai.db
+
+# 初始化数据库
+python datainit.py
+
+# 启动 web 服务
+python run.py
+# python run.py 0.0.0.0 8080
+
+# 尝试访问
+<<<<<<< HEAD
+curl http://localhost:8080/v1/users/
+=======
+curl http://localhost:8080/users/
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+```
+
+## 测试
+
+```bash
+python setup.py test -q
+
+stestr run
+
+tox
+```
+
+## 容器镜像制作和部署
+
+```bash
+docker build -t industrialai .
+
+docker stop industrialai; docker rm industrialai
+
+docker run -d --name industrialai -p 8888:8080 industrialai
+
+curl http://localhost:8888/users/
+<<<<<<< HEAD
+```
+=======
+```
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/README.rst b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/apidoc.json b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/apidoc.json
new file mode 100644
index 0000000000000000000000000000000000000000..37ac42c20295d0aeda937b4513895460ff8e5a7f
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/apidoc.json
@@ -0,0 +1,8 @@
+{
+ "name": "Industrial AI Platform",
+ "version": "0.1.0",
+ "description": "apiDoc basic example",
+ "title": "Custom apiDoc browser title",
+ "url" : "http://106.14.36.119/api",
+ "sampleUrl" : false
+}
\ No newline at end of file
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/config.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f9988d9777ee1b9b7d4e2bb68d0ff037ffb745c
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/config.py
@@ -0,0 +1,54 @@
+# Server Specific Configurations
+server = {
+ 'port': '8080',
+ 'host': '0.0.0.0'
+}
+
+# Pecan Application Configurations
+app = {
+ 'root': 'industrialai.api.controllers.root.RootController',
+ 'modules': ['industrialai.api'],
+ 'static_root': '%(confdir)s/public',
+ 'template_path': '%(confdir)s/industrialai/templates',
+ 'debug': True,
+ 'errors': {
+ 404: '/error/404',
+ '__force_dict__': True
+ }
+}
+
+logging = {
+ 'root': {'level': 'INFO', 'handlers': ['console']},
+ 'loggers': {
+ 'industrialai': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False},
+ 'pecan': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False},
+ 'py.warnings': {'handlers': ['console']},
+ '__force_dict__': True
+ },
+ 'handlers': {
+ 'console': {
+ 'level': 'DEBUG',
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'color'
+ }
+ },
+ 'formatters': {
+ 'simple': {
+ 'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]'
+ '[%(threadName)s] %(message)s')
+ },
+ 'color': {
+ '()': 'pecan.log.ColorFormatter',
+ 'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]'
+ '[%(threadName)s] %(message)s'),
+ '__force_dict__': True
+ }
+ }
+}
+
+# Custom Configurations must be in Python dictionary format::
+#
+# foo = {'bar':'baz'}
+#
+# All configurations are accessible at::
+# pecan.conf
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/datainit.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/datainit.py
new file mode 100644
index 0000000000000000000000000000000000000000..6508a9b618374426aba06d645875f276f09d5c88
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/datainit.py
@@ -0,0 +1,32 @@
+from industrialai.db.model import Base, Role, User
+from sqlalchemy.orm import sessionmaker
+import os
+from sqlalchemy import create_engine
+
+def init_db():
+ DB_URL = os.getenv('db_url') or \
+<<<<<<< HEAD
+ "sqlite:///C:\\Users\\fengdelu\\Desktop\\pecan-swagger\\examples\\industrial-ai-apiserver\\industrialai.db?check_same_thread=False"
+=======
+ "sqlite:///E:\\dhuProject\\pecan-swagger\\examples\\industrial-ai-apiserver\\industrialai.db?check_same_thread=False"
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+ engine = create_engine(DB_URL, echo=True)
+ Base.metadata.drop_all(engine)
+ Base.metadata.create_all(engine)
+ Session = sessionmaker(bind=engine)
+ session = Session()
+
+ session.add_all([
+ Role(title="expert"),
+ Role(title="government"),
+ Role(title="enterpriseAdmin"),
+ Role(title="member"),
+ Role(title="admin"),
+ User(account="admin", pwd="123456", roleId=3),
+ User(account="member", pwd="123456", roleId=4),
+ User(account="root", pwd="123456", roleId=5)
+ ])
+ session.commit()
+
+
+init_db()
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/default.conf b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/default.conf
new file mode 100644
index 0000000000000000000000000000000000000000..a903df6ba9ee8b023f8db2170fcbd82ee6843ead
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/default.conf
@@ -0,0 +1,44 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+
+ #access_log /var/log/nginx/host.access.log main;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ }
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+
+ # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+ #
+ #location ~ \.php$ {
+ # proxy_pass http://127.0.0.1;
+ #}
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ #location ~ \.php$ {
+ # root html;
+ # fastcgi_pass 127.0.0.1:9000;
+ # fastcgi_index index.php;
+ # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
+ # include fastcgi_params;
+ #}
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ #location ~ /\.ht {
+ # deny all;
+ #}
+}
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/nginx.conf b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/nginx.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5e076aadc82d42f40a46a366d4bb69a52051016f
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/deploy/nginx.conf
@@ -0,0 +1,32 @@
+
+user nginx;
+worker_processes auto;
+
+error_log /var/log/nginx/error.log notice;
+pid /var/run/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ include /etc/nginx/conf.d/*.conf;
+}
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai.db b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai.db
new file mode 100644
index 0000000000000000000000000000000000000000..197e6ae3351c6519007cc9a56f7b5be6e4c632ac
Binary files /dev/null and b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai.db differ
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/app.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..f03231acc2fa06482db7bd399b8b23521aac2ded
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/app.py
@@ -0,0 +1,14 @@
+from industrialai.api import hooks
+from pecan import make_app
+
+
+def setup_app(config):
+ app_conf = dict(config.app)
+ app_hooks = [hooks.DBHook()]
+ app = make_app(
+ app_conf.pop('root'),
+ logging=getattr(config, 'logging', {}),
+ hooks=app_hooks,
+ **app_conf
+ )
+ return app
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/root.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/root.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd82b3a291eb8546c47fe2916e0dffedf284f9d2
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/root.py
@@ -0,0 +1,74 @@
+from industrialai.api.controllers.v1 import controller as v1_controller
+from industrialai.api.expose import expose as wsexpose
+from industrialai.db import api
+from industrialai.db.model import User
+from industrialai.pkg.authentication import JWT
+import logging
+from pecan import abort
+from pecan import expose
+from pecan import request
+from wsme import types as wtypes
+logger = logging.getLogger(__name__)
+
+
+class logForm:
+ account = wtypes.text
+ pwd = wtypes.text
+
+
+class checkForm:
+ id = int
+ pwd = wtypes.text
+
+
+class registryForm:
+ account = wtypes.text
+ pwd = wtypes.text
+ phone = wtypes.text
+ mail = wtypes.text
+ name = wtypes.text
+ number = wtypes.text
+
+
+class RootController(object):
+
+ @expose('json')
+ def index(self):
+ return None
+
+ @wsexpose(wtypes.text, body=logForm)
+ def signin(self, logForm):
+ account = logForm.account
+ pwd = logForm.pwd
+ user = api.get_session().query(User). \
+ filter_by(account=account, pwd=pwd).first()
+ if user is None:
+ abort(401)
+ token = JWT.createToken(user.id, user.roleId)
+ return {
+ "token": token
+ }
+
+ @wsexpose(wtypes.text, body=checkForm)
+ def pwdcheck(self, checkForm):
+ try:
+ api.get_session().query(User). \
+ filter_by(id=checkForm.id, pwd=checkForm.pwd).first()
+ except Exception:
+ return "Failure"
+ return "Success"
+
+ @expose('json')
+ def myself(self):
+ token = request.cookies.get("token")
+ message = JWT.deToken(token)
+ if message is None:
+ abort(401)
+ else:
+ uId = message.get("userId")
+ user = api.get_session().query(User). \
+ filter_by(id=uId).first()
+ user.pwd = None
+ return user
+
+ v1 = v1_controller.v1Controller()
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/controller.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..ad47574c472d196d153f520f8246ea6de6e0fbb5
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/controller.py
@@ -0,0 +1,12 @@
+from industrialai.api.controllers.v1 import role as v1_role
+from industrialai.api.controllers.v1 import user as v1_user
+from pecan import expose
+
+
+class v1Controller(object):
+ users = v1_user.UserController()
+ roles = v1_role.RoleController()
+
+ @expose('json')
+ def index(self):
+ return None
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/role.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/role.py
new file mode 100644
index 0000000000000000000000000000000000000000..34bb33bba79ccdbf314933856fd96f5ee55afc03
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/role.py
@@ -0,0 +1,17 @@
+from industrialai.pkg.page import get_page
+from pecan import expose
+from pecan import request
+from pecan.rest import RestController
+
+
+class RoleController(RestController):
+
+ @expose('json')
+ def get_all(self):
+ page_num, page_size = get_page(request.body)
+ db_conn = request.db_conn
+ roles, num = db_conn.list_roles(page_num, page_size)
+ return {
+ "data": roles,
+ "pages": num
+ }
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/user.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/user.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c9132778429c786f2883c67ed67ebc7d8061812
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/controllers/v1/user.py
@@ -0,0 +1,61 @@
+from industrialai.pkg.authentication import JWT
+from industrialai.pkg.page import get_page
+from pecan import abort
+from pecan import expose
+from pecan import request
+from pecan.rest import RestController
+
+
+class UserController(RestController):
+
+ @expose("json")
+ def get(self, **kwargs):
+ page_num, page_size = get_page(request.body)
+ validKeys = ["enterpriseId", "name", "account", "roleId", "status"]
+ kwargs = {k: v for k, v in kwargs.items() if k in validKeys}
+ token = request.cookies.get("token")
+ message = JWT.deToken(token)
+ if message is None:
+ abort(401)
+ else:
+ eId = message.get("enterpriseId")
+ rId = message.get("roleId")
+ if rId != 5:
+ kwargs["enterpriseId"] = eId
+ db_conn = request.db_conn
+ users, pages = db_conn.list_users(page_num, page_size, **kwargs)
+ return {
+ "data": users,
+ "pages": pages
+ }
+
+ @expose("json")
+ def get_one(self, user_id):
+ db_conn = request.db_conn
+ user = db_conn.get_user(user_id)
+ user.pwd = None
+ return user
+
+ @expose("json")
+ def post(self, user):
+ db_conn = request.db_conn
+ db_conn.add_user(user)
+ return "Success"
+
+ @expose("json")
+ def put(self, user):
+ token = request.cookies.get("token")
+ message = JWT.deToken(token)
+ if message is None:
+ abort(401)
+ else:
+ uId = message.get("userId")
+ rId = message.get("roleId")
+ if uId != int(user.get("id")) and rId != 5:
+ abort(401)
+ elif uId == int(user.get("id")):
+ user["roleId"] = None
+ user["phone"] = None
+ db_conn = request.db_conn
+ db_conn.update_user(user)
+ return "Success"
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/expose.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/expose.py
new file mode 100644
index 0000000000000000000000000000000000000000..d84765769124001cb56290e6da81a8426b36a7ce
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/expose.py
@@ -0,0 +1,8 @@
+import wsmeext.pecan as wsme_pecan
+
+
+# 让API返回JSON格式的数据
+def expose(*args, **kwargs):
+ if 'rest_content_types' not in kwargs:
+ kwargs['rest_content_types'] = ('json',)
+ return wsme_pecan.wsexpose(*args, **kwargs)
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/hooks.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/hooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..3395026958792575e46cd1ec52b036ec7b092f23
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/api/hooks.py
@@ -0,0 +1,8 @@
+from industrialai.db import api as db_api
+from pecan import hooks
+
+
+class DBHook(hooks.PecanHook):
+ # Create a db connection instance.
+ def before(self, state):
+ state.request.db_conn = db_api.Connection()
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/api.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..4979abe8e21affdacd6458fa84a2dd3ddaa17a02
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/api.py
@@ -0,0 +1,168 @@
+from industrialai.db import model as db_model
+import logging
+import math
+import os
+from pecan import abort
+from sqlalchemy import create_engine, func
+from sqlalchemy.orm import exc
+<<<<<<< HEAD
+
+=======
+from sqlalchemy.orm import joinedload
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+from sqlalchemy.orm import sessionmaker
+
+logger = logging.getLogger(__name__)
+Domain = "sqlalchemy"
+
+
+_ENGINE = None
+_SESSION_MAKER = None
+
+
+def get_engine():
+ global _ENGINE
+ if _ENGINE is not None:
+ return _ENGINE
+ DB_URL = (
+ os.getenv("db_url") or
+<<<<<<< HEAD
+ "sqlite:///C:\\Users\\fengdelu\\Desktop\\pecan-swagger\\examples\\"
+ "industrial-ai-apiserver\\industrialai.db?check_same_thread=False"
+=======
+ "sqlite:///E:\\dhuProject\\pecan-swagger\\examples\\industrial-ai-apiserver\\industrialai.db?check_same_thread=False"
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+ )
+ _ENGINE = create_engine(DB_URL, echo=True)
+ return _ENGINE
+
+
+def get_session_maker(engine):
+ global _SESSION_MAKER
+ if _SESSION_MAKER is not None:
+ return _SESSION_MAKER
+ _SESSION_MAKER = sessionmaker(bind=engine)
+ return _SESSION_MAKER
+
+
+def get_session():
+ engine = get_engine()
+ maker = get_session_maker(engine)
+ session = maker()
+ return session
+
+
+class Connection(object):
+ def __init__(self):
+ pass
+
+ def get_user(self, user_id):
+ user = None
+ query = get_session().query(db_model.User).filter_by(id=user_id)
+ try:
+ user = query.one()
+ except exc.NoResultFound:
+ logger.error("query by enterpriseId not found ...")
+ abort(404)
+ return user
+
+ def list_users(self, page_num, page_size, **kwargs):
+ users = []
+ # query = get_session().query(db_model.User.account,
+ # db_model.User.name, db_model.User.roleId,
+ # db_model.User.status, db_model.User.create_at, db_model.User.source)
+ query = get_session().query(db_model.User)
+ for k, v in kwargs.items():
+ if v:
+ if k == "name":
+ query = query.filter(
+ db_model.User.name.like("%{keyword}%".
+ format(keyword=v))
+ )
+ elif k == "account":
+ query = query.filter(
+ db_model.User.account.like("%{keyword}%".
+ format(keyword=v))
+ )
+ else:
+ query = query.filter_by(**{k: v})
+ try:
+ users = query.slice((page_num-1)*page_size, page_size).all()
+ num = query.count()
+ pages = math.ceil(num/page_size)
+ for item in users:
+ item.pwd = None
+ except exc.NoResultFound:
+ logger.error("query all user occur error ...")
+ abort(404)
+ return users, pages
+
+ def update_user(self, user):
+ id = user.get("id")
+ logger.info("user.id: %s" % (id))
+ try:
+ session = get_session()
+ session.query(db_model.User).filter_by(id=id).one()
+ for k, v in user.items():
+ if v is not None:
+ session.query(db_model.User).filter_by(id=id). \
+ update({k: v})
+ session.flush()
+ session.commit()
+ except exc.NoResultFound:
+ logger.error("update user occur error ...")
+ abort(404)
+
+ return user
+
+ def delete_user(self, user_id):
+ logger.info("user.id: %s" % (user_id))
+ try:
+ session = get_session()
+ user = session.query(db_model.User).filter_by(id=user_id).first()
+ session.delete(user)
+ session.flush()
+ session.commit()
+ except exc.NoResultFound:
+ logger.error("delete user occur error ...")
+ abort(404)
+
+ def add_user(self, user):
+ db_user = db_model.User(
+ name=user.get("name"),
+ account=user.get("account"),
+ pwd=user.get("pwd"),
+ phone=user.get("phone"),
+ roleId=user.get("roleId"),
+ enterpriseId=user.get("enterpriseId"),
+ status=user.get("status"),
+ )
+ try:
+ session = get_session()
+ if not user.get("groupId") is None:
+ group = (
+ session.query(db_model.Group)
+ .filter_by(id=user.get("groupId"))
+ .one()
+ )
+ db_user.group = group
+ session.add(db_user)
+ session.flush()
+ session.commit()
+ except exc.NoResultFound:
+ logger.error("add user occour error ...")
+ abort(404)
+ # return session.query(db_model.Group).options(
+ # joinedload(db_model.Group.users)).all()
+
+ def list_roles(self, page_num, page_size):
+ roles = []
+ query = get_session().query(db_model.Role)
+ try:
+ roles = query.slice((page_num-1)*page_size, page_size).all()
+ num = get_session().query(func.count(db_model.Role.id)).scalar()
+ pages = math.ceil(num/page_size)
+ except exc.NoResultFound:
+ logger.error("query all roles occur error ...")
+ abort(404)
+ return roles, pages
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/model.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa8e2bd7f9f1ea52c42ffb42e55ab025f115b5eb
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/db/model.py
@@ -0,0 +1,46 @@
+from datetime import datetime
+from sqlalchemy import Column
+from sqlalchemy import DateTime
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import ForeignKey
+from sqlalchemy import Integer
+<<<<<<< HEAD
+from sqlalchemy import String
+=======
+from sqlalchemy.orm import relationship
+from sqlalchemy import String
+from sqlalchemy import Table
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+
+Base = declarative_base()
+
+
+def init_db(_ENGINE):
+ Base.metadata.create_all(_ENGINE)
+
+<<<<<<< HEAD
+
+=======
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
+class User(Base):
+ __tablename__ = 'user'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(32))
+ account = Column(String(32))
+ pwd = Column(String(32))
+ phone = Column(String(12))
+ number = Column(String(19))
+ source = Column(String(10), default="platform")
+ roleId = Column(Integer, ForeignKey("role.id"))
+ status = Column(Integer)
+ create_at = Column(DateTime, default=datetime.now)
+
+
+class Role(Base):
+ __tablename__ = 'role'
+ id = Column(Integer, primary_key=True)
+ title = Column(String(32))
+<<<<<<< HEAD
+=======
+
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/hacking/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/hacking/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/hacking/checks.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/hacking/checks.py
new file mode 100644
index 0000000000000000000000000000000000000000..84207e1d4850ccf05e35534f540aad30715c7aaa
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/hacking/checks.py
@@ -0,0 +1,354 @@
+# Copyright (c) 2016, GohighSec
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import ast
+import re
+
+from hacking import core
+
+
+"""
+Guidelines for writing new hacking checks
+
+ - Use only for Cloudkitty specific tests. OpenStack general tests
+ should be submitted to the common 'hacking' module.
+ - Pick numbers in the range C3xx. Find the current test with
+ the highest allocated number and then pick the next value.
+ - Keep the test method code in the source file ordered based
+ on the C3xx value.
+ - List the new rule in the top level HACKING.rst file
+ - Add test cases for each new rule to cloudkitty/tests/test_hacking.py
+
+"""
+
+UNDERSCORE_IMPORT_FILES = []
+
+_all_log_levels = {'debug', 'error', 'info', 'warning',
+ 'critical', 'exception'}
+# Since _Lx have been removed, we just need to check _()
+translated_logs = re.compile(
+ r"(.)*LOG\.(%(level)s)\(\s*_\(" % {'level': '|'.join(_all_log_levels)})
+
+string_translation = re.compile(r"[^_]*_\(\s*('|\")")
+underscore_import_check = re.compile(r"(.)*import _$")
+underscore_import_check_multi = re.compile(r"(.)*import (.)*_, (.)*")
+# We need this for cases where they have created their own _ function.
+custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*")
+oslo_namespace_imports = re.compile(r"from[\s]*oslo[.](.*)")
+dict_constructor_with_list_copy_re = re.compile(r".*\bdict\((\[)?(\(|\[)")
+assert_no_xrange_re = re.compile(r"\s*xrange\s*\(")
+assert_True = re.compile(r".*assertEqual\(True, .*\)")
+assert_None = re.compile(r".*assertEqual\(None, .*\)")
+no_log_warn = re.compile(r".*LOG.warn\(.*\)")
+
+
+class BaseASTChecker(ast.NodeVisitor):
+ """Provides a simple framework for writing AST-based checks.
+
+ Subclasses should implement visit_* methods like any other AST visitor
+ implementation. When they detect an error for a particular node the
+ method should call ``self.add_error(offending_node)``. Details about
+ where in the code the error occurred will be pulled from the node
+ object.
+
+ Subclasses should also provide a class variable named CHECK_DESC to
+ be used for the human readable error message.
+
+ """
+
+ CHECK_DESC = 'No check message specified'
+
+ def __init__(self, tree, filename):
+ """This object is created automatically by pep8.
+
+ :param tree: an AST tree
+ :param filename: name of the file being analyzed
+ (ignored by our checks)
+ """
+ self._tree = tree
+ self._errors = []
+
+ def run(self):
+ """Called automatically by pep8."""
+ self.visit(self._tree)
+ return self._errors
+
+ def add_error(self, node, message=None):
+ """Add an error caused by a node to the list of errors for pep8."""
+ message = message or self.CHECK_DESC
+ error = (node.lineno, node.col_offset, message, self.__class__)
+ self._errors.append(error)
+
+ def _check_call_names(self, call_node, names):
+ if isinstance(call_node, ast.Call):
+ if isinstance(call_node.func, ast.Name):
+ if call_node.func.id in names:
+ return True
+ return False
+
+
+@core.flake8ext
+def no_translate_logs(logical_line, filename):
+ """Check for 'LOG.*(_('
+
+ Starting with the Pike series, OpenStack no longer supports log
+ translation.
+
+ * This check assumes that 'LOG' is a logger.
+ * Use filename so we can start enforcing this in specific folders instead
+ of needing to do so all at once.
+
+ C313
+ """
+ if translated_logs.match(logical_line):
+ yield(0, "C313 Don't translate logs")
+
+
+class CheckLoggingFormatArgs(BaseASTChecker):
+ """Check for improper use of logging format arguments.
+
+ LOG.debug("Volume %s caught fire and is at %d degrees C and climbing.",
+ ('volume1', 500))
+
+ The format arguments should not be a tuple as it is easy to miss.
+
+ """
+
+ name = "check_logging_format_args"
+ version = "1.0"
+
+ CHECK_DESC = 'C310 Log method arguments should not be a tuple.'
+ LOG_METHODS = [
+ 'debug', 'info',
+ 'warn', 'warning',
+ 'error', 'exception',
+ 'critical', 'fatal',
+ 'trace', 'log'
+ ]
+
+ def _find_name(self, node):
+ """Return the fully qualified name or a Name or Attribute."""
+ if isinstance(node, ast.Name):
+ return node.id
+ elif (isinstance(node, ast.Attribute)
+ and isinstance(node.value, (ast.Name, ast.Attribute))):
+ method_name = node.attr
+ obj_name = self._find_name(node.value)
+ if obj_name is None:
+ return None
+ return obj_name + '.' + method_name
+ elif isinstance(node, str):
+ return node
+ else: # could be Subscript, Call or many more
+ return None
+
+ def visit_Call(self, node):
+ """Look for the 'LOG.*' calls."""
+ # extract the obj_name and method_name
+ if isinstance(node.func, ast.Attribute):
+ obj_name = self._find_name(node.func.value)
+ if isinstance(node.func.value, ast.Name):
+ method_name = node.func.attr
+ elif isinstance(node.func.value, ast.Attribute):
+ obj_name = self._find_name(node.func.value)
+ method_name = node.func.attr
+ else: # could be Subscript, Call or many more
+ return super(CheckLoggingFormatArgs, self).generic_visit(node)
+
+ # obj must be a logger instance and method must be a log helper
+ if (obj_name != 'LOG'
+ or method_name not in self.LOG_METHODS):
+ return super(CheckLoggingFormatArgs, self).generic_visit(node)
+
+ # the call must have arguments
+ if not len(node.args):
+ return super(CheckLoggingFormatArgs, self).generic_visit(node)
+
+ # any argument should not be a tuple
+ for arg in node.args:
+ if isinstance(arg, ast.Tuple):
+ self.add_error(arg)
+
+ return super(CheckLoggingFormatArgs, self).generic_visit(node)
+
+
+@core.flake8ext
+def check_explicit_underscore_import(logical_line, filename):
+ """Check for explicit import of the _ function
+
+ We need to ensure that any files that are using the _() function
+ to translate logs are explicitly importing the _ function. We
+ can't trust unit test to catch whether the import has been
+ added so we need to check for it here.
+ """
+
+ # Build a list of the files that have _ imported. No further
+ # checking needed once it is found.
+ if filename in UNDERSCORE_IMPORT_FILES:
+ pass
+ elif (underscore_import_check.match(logical_line) or
+ underscore_import_check_multi.match(logical_line) or
+ custom_underscore_check.match(logical_line)):
+ UNDERSCORE_IMPORT_FILES.append(filename)
+ elif string_translation.match(logical_line):
+ yield(0, "C321: Found use of _() without explicit import of _ !")
+
+
+class CheckForStrUnicodeExc(BaseASTChecker):
+ """Checks for the use of str() or unicode() on an exception.
+
+ This currently only handles the case where str() or unicode()
+ is used in the scope of an exception handler. If the exception
+ is passed into a function, returned from an assertRaises, or
+ used on an exception created in the same scope, this does not
+ catch it.
+ """
+
+ name = "check_for_str_unicode_exc"
+ version = "1.0"
+
+ CHECK_DESC = ('C314 str() and unicode() cannot be used on an '
+ 'exception. Remove it.')
+
+ def __init__(self, tree, filename):
+ super(CheckForStrUnicodeExc, self).__init__(tree, filename)
+ self.name = []
+ self.already_checked = []
+
+ # Python 2
+ def visit_TryExcept(self, node):
+ for handler in node.handlers:
+ if handler.name:
+ self.name.append(handler.name.id)
+ super(CheckForStrUnicodeExc, self).generic_visit(node)
+ self.name = self.name[:-1]
+ else:
+ super(CheckForStrUnicodeExc, self).generic_visit(node)
+
+ # Python 3
+ def visit_ExceptHandler(self, node):
+ if node.name:
+ self.name.append(node.name)
+ super(CheckForStrUnicodeExc, self).generic_visit(node)
+ self.name = self.name[:-1]
+ else:
+ super(CheckForStrUnicodeExc, self).generic_visit(node)
+
+ def visit_Call(self, node):
+ if self._check_call_names(node, ['str', 'unicode']):
+ if node not in self.already_checked:
+ self.already_checked.append(node)
+ if isinstance(node.args[0], ast.Name):
+ if node.args[0].id in self.name:
+ self.add_error(node.args[0])
+ super(CheckForStrUnicodeExc, self).generic_visit(node)
+
+
+class CheckForTransAdd(BaseASTChecker):
+ """Checks for the use of concatenation on a translated string.
+
+ Translations should not be concatenated with other strings, but
+ should instead include the string being added to the translated
+ string to give the translators the most information.
+ """
+
+ name = "check_for_trans_add"
+ version = "1.0"
+
+ CHECK_DESC = ('C315 Translated messages cannot be concatenated. '
+ 'String should be included in translated message.')
+
+ TRANS_FUNC = ['_']
+
+ def visit_BinOp(self, node):
+ if isinstance(node.op, ast.Add):
+ if self._check_call_names(node.left, self.TRANS_FUNC):
+ self.add_error(node.left)
+ elif self._check_call_names(node.right, self.TRANS_FUNC):
+ self.add_error(node.right)
+ super(CheckForTransAdd, self).generic_visit(node)
+
+
+@core.flake8ext
+def check_oslo_namespace_imports(logical_line, noqa):
+ """'oslo_' should be used instead of 'oslo.'
+
+ C317
+ """
+ if noqa:
+ return
+ if re.match(oslo_namespace_imports, logical_line):
+ msg = ("C317: '%s' must be used instead of '%s'.") % (
+ logical_line.replace('oslo.', 'oslo_'),
+ logical_line)
+ yield(0, msg)
+
+
+@core.flake8ext
+def dict_constructor_with_list_copy(logical_line):
+ """Use a dict comprehension instead of a dict constructor
+
+ C318
+ """
+ msg = ("C318: Must use a dict comprehension instead of a dict constructor"
+ " with a sequence of key-value pairs."
+ )
+ if dict_constructor_with_list_copy_re.match(logical_line):
+ yield (0, msg)
+
+
+@core.flake8ext
+def no_xrange(logical_line):
+ """Ensure to not use xrange()
+
+ C319
+ """
+ if assert_no_xrange_re.match(logical_line):
+ yield(0, "C319: Do not use xrange().")
+
+
+@core.flake8ext
+def validate_assertTrue(logical_line):
+ """Use assertTrue instead of assertEqual
+
+ C312
+ """
+ if re.match(assert_True, logical_line):
+ msg = ("C312: Unit tests should use assertTrue(value) instead"
+ " of using assertEqual(True, value).")
+ yield(0, msg)
+
+
+@core.flake8ext
+def validate_assertIsNone(logical_line):
+ """Use assertIsNone instead of assertEqual
+
+ C311
+ """
+ if re.match(assert_None, logical_line):
+ msg = ("C311: Unit tests should use assertIsNone(value) instead"
+ " of using assertEqual(None, value).")
+ yield(0, msg)
+
+
+@core.flake8ext
+def no_log_warn_check(logical_line):
+ """Disallow 'LOG.warn'
+
+ C320
+ """
+ msg = ("C320: LOG.warn is deprecated, please use LOG.warning!")
+ if re.match(no_log_warn, logical_line):
+ yield(0, msg)
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/authentication.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/authentication.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9ceb2818f2b2e9dde8ceddd754ee09b5b3f2289
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/authentication.py
@@ -0,0 +1,40 @@
+from datetime import datetime
+from datetime import timedelta
+from industrialai.pkg import const
+import jwt
+import os
+
+const.jwtKey = "jwt_key"
+const.defaultKey = "SECRET_KEY"
+
+
+class JWT(object):
+ def createToken(userId, roleId):
+ payload = {
+ 'exp': int((datetime.now() + timedelta(minutes=30)).timestamp()),
+ 'userId': userId,
+ 'roleId': roleId
+ }
+ key = os.getenv(const.jwtKey) or const.defaultKey
+ encodeJwt = jwt.encode(payload, key, algorithm='HS256')
+
+ return encodeJwt
+
+ def deToken(token):
+ data = None
+ secret = os.getenv(const.jwtKey) or const.defaultKey
+ try:
+ data = jwt.decode(token, secret, algorithms=['HS256'])
+ except Exception as e:
+ print(e)
+ # if not data is None:
+ # session = Session()
+ # if session.query(exists().where(Token.token==token)).scalar():
+ # return None
+ return data
+
+ def verifyToken(token):
+ data = JWT.deToken(token)
+ if data is None:
+ return False
+ return True
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/const.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..74be40e98475d2cee64b46539a2d8b4442eb01a1
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/const.py
@@ -0,0 +1,4 @@
+def __setattr__(self, name, value):
+ if name in self.__dict__:
+ raise self.ConstError(f"Can't rebind const ({name})")
+ self.__dict__[name] = value
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/page.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/page.py
new file mode 100644
index 0000000000000000000000000000000000000000..a12ca5f9ebc952fc51d45ad84b8b19c2a492b066
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/pkg/page.py
@@ -0,0 +1,12 @@
+import json
+
+
+def get_page(page):
+ try:
+ page_str = str(page, 'utf-8')
+ page_json = json.loads(page_str)
+ page_num = page_json.get("page_num") or 1
+ page_size = page_json.get("page_size") or 10
+ except Exception:
+ return 1, 10
+ return page_num, page_size
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/__init__.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcdd1d79794fb7ba4a5454489f751b9a3979a2eb
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/__init__.py
@@ -0,0 +1,17 @@
+import os
+from pecan import set_config
+from pecan.testing import load_test_app
+from unittest import TestCase
+
+__all__ = ['FunctionalTest']
+
+
+class FunctionalTest(TestCase):
+ def setUp(self):
+ self.app = load_test_app(os.path.join(
+ os.path.dirname(__file__),
+ 'config.py'
+ ))
+
+ def tearDown(self):
+ set_config({}, overwrite=True)
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/config.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a28f1a3d87c3797eef7f8bbc395fc50e7628efb
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/config.py
@@ -0,0 +1,25 @@
+# Server Specific Configurations
+server = {
+ 'port': '8080',
+ 'host': '0.0.0.0'
+}
+
+# Pecan Application Configurations
+app = {
+ 'root': 'industrialai.api.controllers.root.RootController',
+ 'modules': ['industrialai.api'],
+ 'static_root': '%(confdir)s/../../public',
+ 'template_path': '%(confdir)s/../templates',
+ 'debug': True,
+ 'errors': {
+ '404': '/error/404',
+ '__force_dict__': True
+ }
+}
+
+# Custom Configurations must be in Python dictionary format::
+#
+# foo = {'bar':'baz'}
+#
+# All configurations are accessible at::
+# pecan.conf
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_functional.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_functional.py
new file mode 100644
index 0000000000000000000000000000000000000000..d561f07ddcd6c9ce96c4cccd7560b5110a783dbc
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_functional.py
@@ -0,0 +1,16 @@
+from industrialai.tests import FunctionalTest
+
+
+class TestRootController(FunctionalTest):
+
+ def test_get(self):
+ response = self.app.get('/')
+ assert response.status_int == 200
+
+ def test_search(self):
+ response = self.app.post('/', params={'q': 'RestController'})
+ assert response.status_int == 200
+
+ def test_get_not_found(self):
+ response = self.app.get('/a/bogus/url', expect_errors=True)
+ assert response.status_int == 404
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_units.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_units.py
new file mode 100644
index 0000000000000000000000000000000000000000..573fb682f821e0fb4800a96c80dd90bf4bca1c51
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/industrialai/tests/test_units.py
@@ -0,0 +1,7 @@
+from unittest import TestCase
+
+
+class TestUnits(TestCase):
+
+ def test_units(self):
+ assert 5 * 5 == 25
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/lower-constraints.txt b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/lower-constraints.txt
new file mode 100644
index 0000000000000000000000000000000000000000..63353dc5dbd597076923ae4e782bb8a1e5e5bb2f
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/lower-constraints.txt
@@ -0,0 +1,22 @@
+# requirements
+pecan==1.4.1 # BSD
+WSME==0.11.0 # MIT
+PyJWT==2.3.0 # MIT
+SQLAlchemy==1.4.31 # MIT
+
+# test-requirements
+coverage==5.3 # Apache-2.0
+kombu==5.0.2 # BSD
+ddt==1.4.1 # MIT
+gabbi==2.0.4 # Apache-2.0
+testscenarios==0.5.0 # Apache-2.0/BSD
+stestr==3.0.1 # Apache-2.0
+sphinx==3.3.1 # BSD
+openstackdocstheme==2.2.6
+oslotest==4.4.1 # Apache-2.0
+sphinxcontrib-pecanwsme==0.10.0 # Apache-2.0
+reno==3.2.0
+sphinxcontrib-httpdomain==1.7.0 # Apache-2.0
+doc8==0.8.1 # Apache-2.0
+Pygments==2.7.2 # BSD
+os-api-ref==2.1.0 # Apache-2.0
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/requirements.txt b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3be40053a63a715a1241b6b8cf472fff02a4f152
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/requirements.txt
@@ -0,0 +1,7 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+pecan>=1.4.1 # BSD
+PyJWT>=2.3.0 # MIT
+WSME>=0.11.0 # MIT
+SQLAlchemy>=1.4.31 # MIT
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/run.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/run.py
new file mode 100644
index 0000000000000000000000000000000000000000..292365cb70b39f2f91d20b934324fbf5938761b8
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/run.py
@@ -0,0 +1,20 @@
+import os
+import sys
+from pecan.deploy import deploy
+from wsgiref import simple_server
+
+conf = os.path.join(os.path.dirname(__file__), 'config.py')
+app = deploy(conf)
+
+if __name__ == '__main__':
+ host = '0.0.0.0'
+ port = 8080
+
+ if len(sys.argv) > 1:
+ host = sys.argv[1]
+
+ if len(sys.argv) > 2:
+ port = int(sys.argv[2])
+
+ serve = simple_server.make_server(host, port, app)
+ serve.serve_forever()
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.cfg b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ab44f5c73171af76868c37a53863dfdac778618c
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.cfg
@@ -0,0 +1,6 @@
+[nosetests]
+match=^test
+where=industrialai
+nocapture=1
+cover-package=industrialai
+cover-erase=1
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.py b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c8bb6be781e8d99d82d8efe19bb0a422cd7e07f
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/setup.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+try:
+ from setuptools import setup, find_packages
+except ImportError:
+ from ez_setup import use_setuptools
+ use_setuptools()
+ from setuptools import setup, find_packages
+
+setup(
+ name='industrialai',
+ version='0.1.0',
+ description='Industrial AI API Server',
+ author='',
+ author_email='',
+ install_requires=[
+ "pecan",
+ ],
+ test_suite='industrialai',
+ zip_safe=False,
+ include_package_data=True,
+ packages=find_packages(exclude=['ez_setup'])
+)
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/test-requirements.txt b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/test-requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dcb19c6f2a4560264933f6b3baa83a2c043ebc1e
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/test-requirements.txt
@@ -0,0 +1,16 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+
+# hacking should be first
+hacking>=3.0.1,<3.1.0 # Apache-2.0
+
+coverage>=5.3 # Apache-2.0
+kombu>=5.0.2 # BSD
+ddt>=1.4.1 # MIT
+gabbi>=2.0.4 # Apache-2.0
+testscenarios>=0.5.0 # Apache-2.0/BSD
+stestr>=3.0.1 # Apache-2.0
+oslotest>=4.4.1 # Apache-2.0
+doc8>=0.8.1 # Apache-2.0
+bandit>=1.6.0 # Apache-2.0
diff --git a/Desktop/pecan-swagger/examples/industrial-ai-apiserver/tox.ini b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/tox.ini
new file mode 100644
index 0000000000000000000000000000000000000000..deff8bc2e1b66a31194c7130b85e633ff5b40ab9
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/industrial-ai-apiserver/tox.ini
@@ -0,0 +1,53 @@
+[tox]
+envlist = py3,pep8
+
+[testenv]
+basepython = python3
+allowlist_externals = rm
+setenv = VIRTUAL_ENV={envdir}
+ PYTHONWARNINGS=default::DeprecationWarning
+usedevelop = True
+
+deps = -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
+
+commands =
+ /usr/bin/find . -type f -name "*.py[co]" -delete
+ rm -f /tmp/industrialai.db
+ python datainit.py
+ stestr run {posargs}
+
+[testenv:debug]
+commands = oslo_debug_helper {posargs}
+
+[testenv:pep8]
+commands =
+ flake8 {posargs} industrialai
+
+[testenv:venv]
+commands = {posargs}
+
+[flake8]
+filename = *.py,app.wsgi
+exclude = .git,.venv,.tox,dist,doc,*egg,build,.ropeproject,releasenotes
+
+[flake8:local-plugins]
+extension =
+ C310 = checks:CheckLoggingFormatArgs
+ C311 = checks:validate_assertIsNone
+ C312 = checks:validate_assertTrue
+ C313 = checks:no_translate_logs
+ C314 = checks:CheckForStrUnicodeExc
+ C315 = checks:CheckForTransAdd
+ C317 = checks:check_oslo_namespace_imports
+ C318 = checks:dict_constructor_with_list_copy
+ C319 = checks:no_xrange
+ C320 = checks:no_log_warn_check
+ C321 = checks:check_explicit_underscore_import
+paths = ./industrialai/hacking
+
+[testenv:lower-constraints]
+deps =
+ -c{toxinidir}/lower-constraints.txt
+ -r{toxinidir}/test-requirements.txt
+ -r{toxinidir}/requirements.txt
diff --git a/Desktop/pecan-swagger/examples/myapp.py b/Desktop/pecan-swagger/examples/myapp.py
new file mode 100644
index 0000000000000000000000000000000000000000..88dcc0beeca489ffc3b306012b73a82e5fc0e4d3
--- /dev/null
+++ b/Desktop/pecan-swagger/examples/myapp.py
@@ -0,0 +1,47 @@
+import pecan
+from pecan_swagger import decorators as swagger
+
+
+@swagger.path('messages', 'Messages', 'Root')
+class MessagesController(object):
+
+ @pecan.expose(generic=True, template='messages.html')
+ def index(self):
+ return list()
+
+ @index.when(method='POST')
+ def index_post(self, **kw):
+ print(kw)
+ pecan.redirect('/messages')
+
+
+@swagger.path('profile', 'Profile', 'Root')
+class ProfileController(object):
+
+ @pecan.expose(generic=True, template='profile.html')
+ def index(self):
+ return dict()
+
+ @index.when(method='POST')
+ def index_post(self, **kw):
+ print(kw)
+ pecan.redirect('/profile')
+
+ @pecan.expose(generic=True)
+ def image(self):
+ print('no image uploaded')
+
+ @image.when(method='POST')
+ def image_post(self):
+ print('not supported')
+
+ @pecan.expose()
+ def stats(self):
+ return dict()
+
+
+@swagger.path('/', 'Root')
+class RootController(object):
+
+ profile = ProfileController()
+ messages = MessagesController()
diff --git a/Desktop/pecan-swagger/myapp-doc.py b/Desktop/pecan-swagger/myapp-doc.py
new file mode 100644
index 0000000000000000000000000000000000000000..d7bac65da8c89441434b29e31a893ed38c995874
--- /dev/null
+++ b/Desktop/pecan-swagger/myapp-doc.py
@@ -0,0 +1,8 @@
+#!/bin/env python
+import pprint
+
+from pecan_swagger import utils
+
+
+pp = pprint.PrettyPrinter(indent=2)
+pp.pprint(utils.swagger_build('myapp', '1.0'))
diff --git a/Desktop/pecan-swagger/pecan-swagger b/Desktop/pecan-swagger/pecan-swagger
new file mode 160000
index 0000000000000000000000000000000000000000..cfcc2cd2901628c27326a2e11982396f018327ae
--- /dev/null
+++ b/Desktop/pecan-swagger/pecan-swagger
@@ -0,0 +1 @@
+Subproject commit cfcc2cd2901628c27326a2e11982396f018327ae
diff --git a/Desktop/pecan-swagger/pecan_swagger/__init__.py b/Desktop/pecan-swagger/pecan_swagger/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/pecan_swagger/decorators.py b/Desktop/pecan-swagger/pecan_swagger/decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b8ef4aefaa1919dbeca2d4e6f9715f41b14c6ed
--- /dev/null
+++ b/Desktop/pecan-swagger/pecan_swagger/decorators.py
@@ -0,0 +1,38 @@
+import pecan_swagger.g as g
+
+"""
+decorators module
+
+these decorators are meant to be used by developers who wish to markup
+their pecan applications to produce swagger.
+"""
+
+
+def path(endpoint, name, parent=None):
+ """
+ path decorator
+
+ this decorator should be used on pecan controllers to instruct how
+ they map to routes.
+
+ :param endpoint: the root uri for this controller.
+ :param name: the name of this path.
+ :param parent: an optional path name to indicate a parent/child
+ relationship between this path and another.
+ """
+ def decorator(c):
+ if hasattr(c, '__swag'):
+ raise Exception('{} already has swag'.format(c.__name__))
+ c.__swag = dict(endpoint=endpoint, name=name, parent=parent)
+ g.add_path(c)
+ return c
+ return decorator
+
+
+def method(method):
+ def decorator(m):
+ if hasattr(m, '__swag'):
+ raise Exception('{} already has swag'.format(m.__name__))
+ m.__swag = dict(method=method)
+ return m
+ return decorator
diff --git a/Desktop/pecan-swagger/pecan_swagger/g.py b/Desktop/pecan-swagger/pecan_swagger/g.py
new file mode 100644
index 0000000000000000000000000000000000000000..383c2c6fda52f76e98717f62d5b99155dc7fc12f
--- /dev/null
+++ b/Desktop/pecan-swagger/pecan_swagger/g.py
@@ -0,0 +1,487 @@
+import copy
+import inspect
+
+from pecan import util as p_u
+import wsme.types as wtypes
+import six
+import weakref
+
+
+"""
+global hierarchy module
+
+this module is at the heart of the swagger conversion utility. it
+contains a global "hierarchy" object which will provide a single point
+to assemble an application's swagger output.
+
+there are also several helper functions to assist in building the
+hierarchy of controllers, routes, and methods.
+"""
+
+_hierarchy = {}
+
+_http_methods = {'get': '', 'post': '', 'put': '',
+ 'patch': '', 'delete': '',
+ 'head': '', 'trace': ''}
+
+_all_wsme_types = wtypes.native_types + (
+ wtypes.ArrayType, wtypes.DictType, wtypes.BinaryType,
+ wtypes.IntegerType, wtypes.StringType, wtypes.IPv4AddressType,
+ wtypes.IPv6AddressType, wtypes.UuidType, wtypes.Enum, wtypes.UnsetType)
+
+_definitions = {}
+
+
+def add_path(c):
+ """adds a named controller to the hierarchy."""
+ if _hierarchy.get(c.__swag['name']):
+ raise Exception(
+ 'name {} already exists in hierarchy'.format(c.__swag['name']))
+ _hierarchy[c.__swag['name']] = c
+
+
+def build_path(swaginfo):
+ """return the route path for a swag metadata item."""
+ if swaginfo.get('parent') is not None:
+ return path_join(build_path(get_swag(swaginfo.get('parent'))),
+ swaginfo.get('endpoint'))
+ return swaginfo.get('endpoint')
+
+
+def get_controller_paths(controllers, wsme_defs):
+ """
+ get a list of paths with methods
+
+ returns a list of tuples (controller path, methods).
+ """
+ def get_methods_for_generic(name):
+ methods = []
+ generic_handlers = lc.get(name).get('generic_handlers', {})
+ for method, func in generic_handlers.items():
+ if method == 'DEFAULT':
+ methods.append('get')
+ else:
+ methods.append(method.lower())
+ # TODO drill down through decorators to get true function name
+ truename = getrealname(func)
+ del lc[truename]
+ return methods
+
+ def append_methods_for_specific(name, tpl):
+ paths.append(tpl)
+ del lc[name]
+
+ lc = copy.deepcopy(controllers)
+ paths = []
+ # TODO incorporate method decorator, removing functions marked
+ if lc.get('index'):
+ for method in get_methods_for_generic('index'):
+ paths.append(('', (method, {})))
+
+ # for REST controller
+ for method, path in _http_methods.items():
+ if lc.get(method):
+ spec = wsme_defs.get(method, {}) # add wsme definitions
+ append_methods_for_specific(method, (path, (method, spec)))
+
+ if lc.get('get_all'):
+ # add wsme definitions
+ spec = wsme_defs.get('get_all') # add wsme definitions
+ append_methods_for_specific('get_all', ('', ('get', spec)))
+ if lc.get('get_one'):
+ # add wsme definitions
+ spec = wsme_defs.get('get_one') # add wsme definitions
+ append_methods_for_specific('get_one', ('', ('get', spec)))
+
+ if lc.get('_default'):
+ append_methods_for_specific('_default', ('', ('*', {})))
+ if lc.get('_route'):
+ append_methods_for_specific('_route', ('', ('*', {})))
+ if lc.get('_lookup'):
+ del lc['_lookup']
+
+ if lc.get('_custom_actions'):
+ for ca, ca_method in lc['_custom_actions'].items():
+ spec = wsme_defs.get(ca) # add wsme definitions
+ for m in ca_method:
+ paths.append((ca, (m.lower(), spec)))
+ del lc['_custom_actions']
+
+ generic_controllers = [c for c in lc if lc[c].get('generic')]
+ for controller in generic_controllers:
+ for method in get_methods_for_generic(controller):
+ paths.append((controller, (method, {})))
+ for controller in lc:
+ if controller not in [path[0] for path in paths]:
+ paths.append((controller, ('get', {})))
+ return paths
+
+
+def get_wsme_defs(name):
+
+ def datatype_to_type_and_format(datatype):
+ tf = {}
+ if datatype == 'uuid' or datatype == 'ipv4address' \
+ or datatype == 'ipv6address' or datatype == 'date' \
+ or datatype == 'time':
+ tf['type'] = 'string'
+ tf['format'] = datatype
+ elif datatype == 'datetime':
+ tf['type'] = 'string'
+ tf['format'] = 'date-time'
+ elif datatype == 'binary' or datatype == 'bytes':
+ # base64 encoded characters
+ tf['type'] = 'string'
+ tf['format'] = 'byte'
+ elif datatype == 'array' or datatype == 'boolean' \
+ or datatype == 'integer' or datatype == 'string':
+ # no format
+ tf['type'] = datatype
+ elif datatype == 'float':
+ # number
+ tf['type'] = 'number'
+ tf['format'] = datatype
+ elif datatype == 'unicode' or datatype == 'str' \
+ or datatype == 'text':
+ # primitive, no format
+ tf['type'] = 'string'
+ elif datatype == 'int' or datatype == 'decimal':
+ # primitive, no format
+ tf['type'] = 'integer'
+ elif datatype == 'bool':
+ # primitive, no format
+ tf['type'] = 'boolean'
+ elif datatype == 'enum':
+ tf['type'] = 'enum'
+ elif datatype == 'unset' or datatype == 'none':
+ tf['type'] = None
+ elif datatype == 'dict':
+ tf['type'] = 'object'
+ else:
+ tf['type'] = 'object'
+ return tf
+
+ def class_to_name_str(c):
+ return (('%s' % c).replace('<', '').replace('>', '')
+ .replace('class ', '').replace('\'', '')
+ .split(' ', 1)[0].rsplit('.', 1)[-1])
+
+ def conv_class_name_to_type_str(cn):
+ type_str = ''
+ if cn.endswith('Type'):
+ type_str = cn[:-4]
+ elif cn == 'str':
+ type_str = 'string'
+ else:
+ type_str = cn
+ type_str = type_str.lower()
+ return type_str
+
+ def get_type_str(obj, src_dict=None):
+ type_str = ''
+ if hasattr(obj, '__name__'):
+ type_str = obj.__name__
+ else:
+ type_str = obj.__class__.__name__
+ type_str = conv_class_name_to_type_str(type_str)
+
+ tf = datatype_to_type_and_format(type_str)
+
+ if hasattr(obj, 'basetype') and \
+ (obj.__class__ not in _all_wsme_types or type_str == 'enum'):
+ # UserType or Enum
+ tf['type'] = get_type_str(obj.basetype)
+
+ if isinstance(src_dict, dict):
+ # if dict is in args, set 'type' and 'format' into the dict and
+ # return
+ src_dict.update({'type': tf['type']})
+ if 'format' in tf:
+ src_dict.update({'format': tf['format']})
+
+ # get datatype options. ex.) min_length, minimum, ..
+ for k, v in inspect.getmembers(obj):
+ if ((k == 'minimum' or k == 'maxmum'
+ or k == 'min_length' or k == 'max_length')
+ and v is not None):
+ src_dict[to_swag_key(k)] = v
+ elif k == 'pattern' and v is not None:
+ src_dict[to_swag_key(k)] = v.pattern
+ # TODO(shu-mutou): this should be removed for production.
+ # else:
+ # src_dict[to_swag_key(k)] = v
+
+ if type_str == 'enum':
+ # EnumType use 'set' that doesn't have sequence.
+ # So use 'sorted' for keeping static output.
+ src_dict['enum'] = sorted([v for v in obj.values])
+
+ # return 'type' only
+ return tf['type']
+
+ def to_swag_key(key):
+ keys = {
+ 'doc': 'description',
+ 'arguments': 'parameters',
+ 'return_type': 'schema',
+ 'datatype': 'type',
+ 'mandatory': 'required',
+ 'sample': 'examples',
+ 'readonly': 'readOnly',
+ 'min_length': 'minLength',
+ 'max_length': 'maxLength',
+ }
+ if key in keys:
+ return keys[key]
+ else:
+ return key
+
+ def get_wm_item_prop(item, isparams=False):
+ # Add prop into 'properties' and 'required' in 'Items Object'
+ # 'Items Object' can be part of 'Schema Object' or 'Items Object',
+ # and can use recursively
+ prop_dict = {}
+ # TODO(shu-mutou): this should be removed for production.
+ # prop_dict['obj'] = inspect.getmembers(item)
+
+ _item = item
+ if wtypes.iscomplex(item):
+ _item = weakref.ref(item)
+
+ for a, i in inspect.getmembers(_item):
+ if a == 'datatype':
+ datatype = get_type_str(i, prop_dict)
+ if datatype == 'array':
+ # if array, do recursively
+ prop_dict['items'] = inspect_wm_schema(i.item_type)
+ elif datatype == 'object':
+ # if obuject, do recursively
+ prop_dict['items'] = inspect_wm_schema(i)
+ elif a == 'default' and i:
+ prop_dict[to_swag_key(a)] = i
+ elif a == 'name' and isparams:
+ prop_dict[to_swag_key(a)] = i
+ elif a == 'mandatory' and i:
+ prop_dict[to_swag_key(a)] = i
+ elif a == 'readonly' and i:
+ prop_dict[to_swag_key(a)] = i
+ elif a == 'doc' and i is not None:
+ prop_dict[to_swag_key(a)] = i
+
+ if isparams and prop_dict['type'] in ['object', 'array']:
+ prop_dict['schema'] = {'items': prop_dict['items'],
+ 'type': prop_dict['type']}
+ del prop_dict['type']
+ del prop_dict['items']
+ return prop_dict
+
+ def get_wsattr_and_wsproperty(obj):
+ ws_dict = {}
+ for key, value in inspect.getmembers(obj):
+ if (key[0] != '_' and
+ (value.__class__.__name__ == 'wsattr'
+ or value.__class__.__name__ == 'wsproperty')):
+ ws_dict[key] = value
+ return ws_dict
+
+ def inspect_wm_schema(schema_obj, isparams=False):
+ schema_dict = {}
+ # TODO(shu-mutou): this should be removed for production.
+ # schema_dict['obj'] = get_wsattr_and_wsproperty(schema_obj)
+ ws_len = len(get_wsattr_and_wsproperty(schema_obj))
+ model_name = class_to_name_str(schema_obj)
+
+ for key, obj in inspect.getmembers(schema_obj):
+ if (key[0] != '_' and
+ (obj.__class__.__name__ == 'wsattr'
+ or obj.__class__.__name__ == 'wsproperty')):
+ # TODO(shu-mutou): this should be removed for production.
+ # _definitions[model_name][to_swag_key(key)] = \
+ # {'obj': inspect.getmembers(obj)}
+
+ if ws_len == 1:
+ # single schema
+ schema_dict = get_wm_item_prop(obj, isparams)
+ else:
+ # multi property schema
+ schema_dict.update({'type': 'object'})
+ # properties
+ if 'items' not in schema_dict:
+ schema_dict['items'] = {'properties': {}}
+ prop = {key: get_wm_item_prop(obj, isparams)}
+ # required as array of string
+ if 'required' in prop[key] and prop[key]['required'] \
+ and isinstance(prop[key]['required'], bool):
+ if 'required' not in schema_dict:
+ schema_dict['required'] = []
+ schema_dict['required'].append(key)
+ del prop[key]['required']
+ schema_dict['items']['properties'].update(prop)
+
+ if schema_obj not in _all_wsme_types:
+ if model_name not in _definitions:
+ _definitions[model_name] = schema_dict
+ schema_dict = {'$ref': "#/definitions/%s" % model_name}
+
+ return schema_dict
+
+ def get_wm_def(o):
+ wsme = {'description': ''}
+ for w, d in inspect.getmembers(o):
+ if w == 'arguments':
+ wsme[to_swag_key(w)] = []
+ for arg in d:
+ # set each 'Parameter Object', it can include
+ # 'Items Object' recursively
+ item_dict = get_wm_item_prop(arg, True)
+ # TODO: MUST be set one of
+ # 'body|query|path|header|formData'
+ item_dict['in'] = 'query'
+ wsme[to_swag_key(w)].append(item_dict)
+ # 'body' should be set due to existing 'body' option
+ # in WSME expose
+ if o.body_type is not None:
+ # if body is set, last arg is it.
+ wsme[to_swag_key(w)][-1]['in'] = 'body'
+ elif w == 'doc' and d:
+ wsme[to_swag_key(w)] = d
+ elif w == 'return_type':
+ wsme['responses'] = {'status': {'description': ''}}
+ if d:
+ if d in _all_wsme_types:
+ # d is set single WSME type class or implicit type
+ wsme['responses']['status'][to_swag_key(w)] = (
+ datatype_to_type_and_format(
+ conv_class_name_to_type_str(
+ class_to_name_str(d))))
+ else:
+ # d is model class
+ wsme['responses']['status'][to_swag_key(w)] = (
+ inspect_wm_schema(d, False))
+ doc = inspect.getdoc(d)
+ if doc is not None:
+ wsme['responses']['status']['description'] = doc
+ elif w == 'status_code':
+ wsme['responses'][d] = wsme['responses']['status']
+ del wsme['responses']['status']
+ # TODO(shu-mutou): this should be removed for production.
+ # elif w == 'body_type':
+ # wsme[to_swag_key(w)] = get_type_str(d)
+ # elif w == 'extra_options' or w == 'ignore_extra_args' \
+ # or w == 'name' or w == 'pass_request':
+ # wsme[to_swag_key(w)] = d
+ # else:
+ # wsme[to_swag_key(w)] = d
+ return wsme
+
+ c = _hierarchy[name]
+ wsme_defs = {}
+ for k, v in inspect.getmembers(c):
+ if p_u.iscontroller(v):
+ for m, o in inspect.getmembers(v):
+ if m == "_wsme_definition":
+ wsme_defs[k] = get_wm_def(o)
+
+ # TODO(shu-mutou): this should be removed for production.
+ # output wsme info into files by each controller for dev
+ # import pprint
+ # with open(name + '.txt', 'w') as fout:
+ # pprint.pprint(wsme_defs, stream=fout, indent=2)
+
+ return wsme_defs
+
+
+def get_controllers(name):
+ """
+ get all the controllers associated with a path
+
+ returns a dictionary of controllers indexed by their names.
+ """
+ c = _hierarchy[name]
+ controllers = {k: p_u._cfg(v)
+ for k, v in c.__dict__.items() if p_u.iscontroller(v)}
+ cacts = {k: v for k, v in c.__dict__.items() if k == '_custom_actions'}
+ if len(cacts):
+ controllers.update(cacts)
+ return controllers
+
+
+def get_paths():
+ """
+ return all the registered paths
+
+ loops through the hierarchy and retuns a list of tuples containing the
+ paths and their methods.
+
+ :returns: [(path, methods), ...]
+ """
+ pathlist = []
+ for name in _hierarchy:
+ fullpath = build_path(get_swag(name))
+ controllers = get_controllers(name)
+ wsme_defs = get_wsme_defs(name)
+ paths = get_controller_paths(controllers, wsme_defs)
+ for path in paths:
+ ptuple = (path_join(fullpath, path[0]), path[1])
+ pathlist.append(ptuple)
+ return pathlist
+
+
+def get_swag(name):
+ """return the swag metadata from an named controller."""
+ return _hierarchy.get(name).__swag
+
+
+def getrealname(method):
+ """attempt to get a method's real name."""
+ argspec = inspect.getargspec(method)
+ args = argspec[0]
+ if args and args[0] == 'self':
+ return method.__name__
+ if hasattr(method, '__func__'):
+ method = method.__func__
+
+ func_closure = six.get_function_closure(method)
+
+ # NOTE(sileht): if the closure is None we cannot look deeper,
+ # so return actual argspec, this occurs when the method
+ # is static for example.
+ if func_closure is None:
+ return method.__name__
+
+ closure = next(
+ (
+ c for c in func_closure if six.callable(c.cell_contents)
+ ),
+ None
+ )
+ method = closure.cell_contents
+ return getrealname(method)
+
+
+def methods_get(name):
+ """get all the methods for a named controller."""
+ c = _hierarchy[name]
+ mlist = []
+ if hasattr(c, 'index') and p_u.iscontroller(c.index):
+ cfg = p_u._cfg(c.index)
+ if cfg.get('generic_handlers').get('DEFAULT'):
+ mlist.append('get')
+ mlist += cfg.get('allowed_methods')
+ for i in c.__dict__:
+ ii = getattr(c, i)
+ if hasattr(ii, '__swag'):
+ m = ii.__swag.get('method')
+ if m is not None:
+ mlist.append(m)
+ return mlist
+
+
+def path_join(part1, part2):
+ """join two url paths."""
+ if len(part2) == 0:
+ return part1
+ sep = '/'
+ if part1[-1] == sep:
+ sep = ''
+ return part1 + sep + part2
diff --git a/Desktop/pecan-swagger/pecan_swagger/utils.py b/Desktop/pecan-swagger/pecan_swagger/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa3d3b6155a6322005791e833abb278cd6ed2a01
--- /dev/null
+++ b/Desktop/pecan-swagger/pecan_swagger/utils.py
@@ -0,0 +1,64 @@
+from pecan_swagger import g as g
+
+
+"""
+utility function module
+
+this module contains the utility functions to assemble the swagger
+dictionary object. they can be consumed by end-user applications to
+build up swagger objects for applications.
+
+functions:
+swagger_build -- build a full swagger dictionary
+"""
+
+
+def swagger_build(title, version):
+ swag = dict()
+ swag['swagger'] = '2.0'
+ swag['info'] = dict(title=title, version=version)
+ swag['consumes'] = []
+ swag['produces'] = []
+ swag['paths'] = {}
+ for p in g.get_paths():
+ if p[0] not in swag['paths']:
+ swag['paths'][p[0]] = _tuple_to_dict(p[1])
+ elif len(p[1]) > 0:
+ swag['paths'][p[0]].update(_tuple_to_dict(p[1]))
+ swag['definitions'] = g._definitions
+ return swag
+
+
+def _tuple_to_dict(tpl):
+ """Convert tuple to dictionary
+
+ each tuple must have key and value.
+ This function arrows taple including lists.
+
+ ex.) acceptable taple
+ OK: ('/', ('get',(('desc',{}),('res',{}),('params',['id','name']))))
+ NG: ('/', ('get',('desc','res',('params',['id','name']))))
+ """
+ if isinstance(tpl, list):
+ d = []
+ for e in tpl:
+ d.append(_tuple_to_dict(e))
+ elif isinstance(tpl, tuple):
+ d = {}
+ if isinstance(tpl[0], tuple):
+ # tuple has some child tuple
+ for e in tpl:
+ d[e[0]] = _tuple_to_dict(e[1])
+ elif isinstance(tpl[0], list):
+ # list member should be processed recursively
+ d = _tuple_to_dict(tpl[0])
+ else:
+ if len(tpl) == 2:
+ # single tuple node
+ d[tpl[0]] = _tuple_to_dict(tpl[1])
+ else:
+ raise Exception(tpl)
+ else:
+ # value or dict
+ d = tpl
+ return d
diff --git a/Desktop/pecan-swagger/setup.cfg b/Desktop/pecan-swagger/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3c6e79cf31da1c0433d2fa666bf50b53f6359f26
--- /dev/null
+++ b/Desktop/pecan-swagger/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal=1
diff --git a/Desktop/pecan-swagger/setup.py b/Desktop/pecan-swagger/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..7de524de28dc0039e039e78cf4399b6c16ce2466
--- /dev/null
+++ b/Desktop/pecan-swagger/setup.py
@@ -0,0 +1,32 @@
+import setuptools
+
+from codecs import open
+from os import path
+
+
+here = path.abspath(path.dirname(__file__))
+# Get the long description from the README file
+with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
+ long_description = f.read()
+
+
+setuptools.setup(
+ name='pecan-swagger',
+ version='0.1.2',
+ description='A project to produce swagger from pecan',
+ long_description=long_description,
+ url='https://github.com/elmiko/pecan-swagger',
+ author='Michael McCune',
+ author_email='msm@opbstudios.com',
+ license='BSD',
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Programming Language :: Python :: 3.6',
+ ],
+ keywords='pecan swagger development',
+ packages=[
+ 'pecan_swagger',
+ ],
+)
diff --git a/Desktop/pecan-swagger/tests/__init__.py b/Desktop/pecan-swagger/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/tests/resources/__init__.py b/Desktop/pecan-swagger/tests/resources/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Desktop/pecan-swagger/tests/resources/example_app.py b/Desktop/pecan-swagger/tests/resources/example_app.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d768676da837846e44d2ce41d7864adefb5e47f
--- /dev/null
+++ b/Desktop/pecan-swagger/tests/resources/example_app.py
@@ -0,0 +1,57 @@
+import pecan
+from pecan import rest
+from pecan_swagger import decorators as swagger
+
+
+@swagger.path('api', 'API', 'Root')
+class APIController(rest.RestController):
+
+ @pecan.expose()
+ def get(self):
+ pass
+
+
+@swagger.path('messages', 'Messages', 'Root')
+class MessagesController(object):
+
+ @pecan.expose(generic=True, template='messages.html')
+ def index(self):
+ return list()
+
+ @index.when(method='POST')
+ def index_post(self, **kw):
+ print(kw)
+ pecan.redirect('/messages')
+
+
+@swagger.path('profile', 'Profile', 'Root')
+class ProfileController(object):
+
+ @pecan.expose(generic=True, template='profile.html')
+ def index(self):
+ return dict()
+
+ @index.when(method='POST')
+ def index_post(self, **kw):
+ print(kw)
+ pecan.redirect('/profile')
+
+ @pecan.expose(generic=True)
+ def image(self):
+ print('no image uploaded')
+
+ @image.when(method='POST')
+ def image_post(self):
+ print('not supported')
+
+ @pecan.expose()
+ def stats(self):
+ return dict()
+
+
+@swagger.path('/', 'Root')
+class RootController(object):
+
+ profile = ProfileController()
+ messages = MessagesController()
+ api = APIController()
diff --git a/Desktop/pecan-swagger/tests/resources/example_wsme_app.py b/Desktop/pecan-swagger/tests/resources/example_wsme_app.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7c81007ba708f596dfac70c04c6deaea09504d2
--- /dev/null
+++ b/Desktop/pecan-swagger/tests/resources/example_wsme_app.py
@@ -0,0 +1,50 @@
+import pecan
+from pecan import rest
+from pecan_swagger import decorators as swagger
+import wsme
+from wsme import types as wtypes
+from wsmeext.pecan import wsexpose
+
+
+class MessageModel(object):
+ id = wtypes.StringType(min_length=1, max_length=255)
+ message = wtypes.StringType(min_length=1, max_length=255)
+ message_size = wtypes.IntegerType(minimum=1)
+ message_from = wtypes.Enum(str, '1.OSOMATSU', '2.KARAMATSU',
+ '3.CHOROMATSU', '4.ICHIMATSU',
+ '5.JUSHIMATSU', '6.TODOMATSU')
+
+
+class MessageCollection(object):
+ messages = [MessageModel]
+
+@swagger.path('wsmemessages', 'WsmeMessages', 'WsmeRoot')
+class MessagesController(rest.RestController):
+
+ _custom_actions = {
+ 'detail': ['GET'],
+ }
+
+ @wsexpose(MessageCollection)
+ def get_all(self):
+ return MessageCollection()
+
+ @wsexpose(MessageModel, wtypes.text)
+ def get_one(self, id):
+ return MessageModel()
+
+ @wsexpose(MessageModel, wtypes.text, status_code=201)
+ def post(self, message):
+ return MessageModel()
+
+ @wsexpose(None, wtypes.text, status_code=204)
+ def delete(self, id):
+ pass
+
+ @wsexpose(MessageModel, wtypes.text)
+ def detail(self, id):
+ return MessageModel()
+
+@swagger.path('/', 'WsmeRoot')
+class RootController(rest.RestController):
+ pass
diff --git a/Desktop/pecan-swagger/tests/test_utils.py b/Desktop/pecan-swagger/tests/test_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..692300fc2f0689db7a37b073ad50861a8f2f0915
--- /dev/null
+++ b/Desktop/pecan-swagger/tests/test_utils.py
@@ -0,0 +1,216 @@
+import unittest
+
+from pecan_swagger import utils
+
+
+class TestUtils(unittest.TestCase):
+ def test_swagger_build(self):
+ from .resources import example_app
+
+ expected = {
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0",
+ "title": "example_app"
+ },
+ "produces": [],
+ "consumes": [],
+ "definitions": {},
+ "paths": {
+ "/api": {
+ "get": {}
+ },
+ "/messages": {
+ "get": {},
+ "post": {}
+ },
+ "/profile": {
+ "get": {},
+ "post": {}
+ },
+ "/profile/image": {
+ "get": {},
+ "post": {}
+ },
+ "/profile/stats": {
+ "get": {}
+ }
+ }
+ }
+
+ actual = utils.swagger_build('example_app', '1.0')
+ self.assertDictEqual(expected, actual)
+
+ def test_swagger_build_wsme(self):
+ from .resources import example_wsme_app
+
+ expected = \
+ {
+ "consumes": [],
+ "definitions": {
+ "MessageCollection": {
+ "items": {
+ "$ref": "#/definitions/MessageModel"
+ },
+ "type": "array"
+ },
+ "MessageModel": {
+ "items": {
+ "properties": {
+ "id": {
+ "maxLength": 255,
+ "minLength": 1,
+ "type": "string"
+ },
+ "message": {
+ "maxLength": 255,
+ "minLength": 1,
+ "type": "string"
+ },
+ "message_from": {
+ "enum": [
+ "1.OSOMATSU",
+ "2.KARAMATSU",
+ "3.CHOROMATSU",
+ "4.ICHIMATSU",
+ "5.JUSHIMATSU",
+ "6.TODOMATSU"
+ ],
+ "type": "string"
+ },
+ "message_size": {
+ "minimum": 1,
+ "type": "integer"
+ }
+ }
+ },
+ "type": "object"
+ }
+ },
+ "info": {
+ "title": "example_wsme_app",
+ "version": "1.0"
+ },
+ "paths": {
+ "/api": {
+ "get": {}
+ },
+ "/messages": {
+ "get": {},
+ "post": {}
+ },
+ "/profile": {
+ "get": {},
+ "post": {}
+ },
+ "/profile/image": {
+ "get": {},
+ "post": {}
+ },
+ "/profile/stats": {
+ "get": {}
+ },
+ "/wsmemessages": {
+ "get": {
+ "description": "",
+ "parameters": [],
+ "responses": {
+ 200: {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MessageCollection"
+ }
+ }
+ }
+ },
+ "post": {
+ "description": "",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "message",
+ "required": True,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ 201: {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MessageModel"
+ }
+ }
+ }
+ }
+ },
+ "/wsmemessages/": {
+ "delete": {
+ "description": "",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "id",
+ "required": True,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ 204: {
+ "description": ""
+ }
+ }
+ },
+ "get": {
+ "description": "",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "id",
+ "required": True,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ 200: {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MessageModel"
+ }
+ }
+ }
+ }
+ },
+ "/wsmemessages/detail": {
+ "get": {
+ "description": "",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "id",
+ "required": True,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ 200: {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MessageModel"
+ }
+ }
+ }
+ }
+ }
+ },
+ "produces": [],
+ "swagger": "2.0"
+ }
+
+ actual = utils.swagger_build('example_wsme_app', '1.0')
+
+ import codecs, json
+ fout = codecs.open('example_wsme_app.json', 'w', 'utf_8')
+ json.dump(actual, fout, sort_keys=True, indent=2)
+
+ self.maxDiff = None
+ self.assertDictEqual(expected, actual)
diff --git a/Desktop/pecan-swagger/tox.ini b/Desktop/pecan-swagger/tox.ini
new file mode 100644
index 0000000000000000000000000000000000000000..d2a246569d731a667ab8533723bb4c430d269ef1
--- /dev/null
+++ b/Desktop/pecan-swagger/tox.ini
@@ -0,0 +1,16 @@
+[tox]
+envlist = py3
+
+[testenv]
+deps =
+ nose2
+ pecan
+ WSME
+ flake8
+commands =
+ flake8 pecan_swagger
+<<<<<<< HEAD
+ nose2 -v
+=======
+ nose2
+>>>>>>> cfcc2cd2901628c27326a2e11982396f018327ae