From 77d14a11e0d013dcec22b370bffd062650615809 Mon Sep 17 00:00:00 2001 From: "Xuan (Sean) Hu" Date: Fri, 17 Jan 2020 17:43:17 +0800 Subject: [PATCH] Version 2.1.0, repo migration with better VERSION. (#1) * Version 0.1.0, repo migration with better VERSION. * Add more badges in README and change project url in setup.py. * Bump version to 2.1.0 * Add Chinese version README. * Minor typo. --- .gitignore | 3 ++ .pylintrc | 2 + MANIFEST.in | 3 ++ Makefile | 35 +++++++++++++ README-en.md | 74 ++++++++++++++++++++++++++++ README.md | 74 ++++++++++++++++++++++++++++ VERSION | 1 + setup.py | 66 +++++++++++++++++++++++++ tox.ini | 13 +++++ wxpusher/__init__.py | 19 +++++++ wxpusher/exceptions.py | 16 ++++++ wxpusher/tests/__init__.py | 16 ++++++ wxpusher/tests/config.sample.py | 15 ++++++ wxpusher/tests/exceptions.py | 13 +++++ wxpusher/tests/test_create_qrcode.py | 30 +++++++++++ wxpusher/tests/test_query_message.py | 23 +++++++++ wxpusher/tests/test_query_user.py | 31 ++++++++++++ wxpusher/tests/test_send_message.py | 32 ++++++++++++ wxpusher/wxpusher.py | 68 +++++++++++++++++++++++++ 19 files changed, 534 insertions(+) create mode 100644 .pylintrc create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README-en.md create mode 100644 README.md create mode 100644 VERSION create mode 100644 setup.py create mode 100644 tox.ini create mode 100644 wxpusher/__init__.py create mode 100644 wxpusher/exceptions.py create mode 100644 wxpusher/tests/__init__.py create mode 100644 wxpusher/tests/config.sample.py create mode 100644 wxpusher/tests/exceptions.py create mode 100644 wxpusher/tests/test_create_qrcode.py create mode 100644 wxpusher/tests/test_query_message.py create mode 100644 wxpusher/tests/test_query_user.py create mode 100644 wxpusher/tests/test_send_message.py create mode 100644 wxpusher/wxpusher.py diff --git a/.gitignore b/.gitignore index b6e4761..efeb4e1 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + +# Custom +config.py diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..233c1c2 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[SIMILARITIES] +ignore-imports=yes diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..389b286 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE +include README.md +include VERSION diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7641742 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +.PHONY: clean install dev lint pycodestyle pyflakes pylint test dist upload + +clean: + find . -name '*.pyc' -print0 | xargs -0 rm -f + find . -name '*.swp' -print0 | xargs -0 rm -f + find . -name '.DS_Store' -print0 | xargs -0 rm -rf + find . -name '__pycache__' -print0 | xargs -0 rm -rf + -rm -rf build dist *.egg-info + -rm -rf .tox .coverage + +install: + pip install . + +dev: + pip install .[dev] + +lint: pycodestyle pyflakes pylint + +pycodestyle: + -pycodestyle setup.py wxpusher + +pyflakes: + -pyflakes setup.py wxpusher + +pylint: + -pylint setup.py wxpusher + +test: + tox + +dist: clean + python setup.py sdist bdist_wheel + +upload: + twine upload dist/* diff --git a/README-en.md b/README-en.md new file mode 100644 index 0000000..00cd908 --- /dev/null +++ b/README-en.md @@ -0,0 +1,74 @@ +# WxPusher + +[![PyPI version](https://badge.fury.io/py/wxpusher.svg)](https://badge.fury.io/py/wxpusher) +[![PyPI license](https://img.shields.io/pypi/l/wxpusher.svg)](https://pypi.python.org/pypi/wxpusher/) +[![Python Versions](https://img.shields.io/pypi/pyversions/wxpusher.svg)](https://pypi.python.org/pypi/wxpusher/) +[![Downloads](https://pepy.tech/badge/wxpusher)](https://pepy.tech/project/wxpusher) + +WxPusher Python SDK. + +*Read this in other languages: [English](README-en.md), [简体中文](README.md).* + +## Getting Started + +### Installation + +```shell +pip install -U wxpusher +``` + +### Usage + +```python +from wxpusher import WxPusher +WxPusher.send_message('', '', '') +WxPusher.query_message('') +WxPusher.create_qrcode('', '', '') +WxPusher.query_user('', '', '') +``` + +## Running the tests + +### Setup configuration + +Configuration is necessary since the tests depend on VALID `appToken` and `uids`. + +Frist, copy the configuration sample `config.sample.py` under `python/wxpusher/tests/` to `config.py` + +```shell +cd python/wxpusher/tests +cp config.sample.py config.py +``` + +Then, fill in the corresponding information in `config.py`. + +### Trigger the tests + +With proper configuration, you can run the tests with tox + +```shell +tox +``` + +or nose directly + +```shell +nosetests +``` + +## TODO + +- [x] Basic structure with PyPI uploaded. +- [x] Send Message. +- [x] Query Message. +- [x] Create QRCode. +- [x] Query User. +- [ ] More client validators. +- [ ] Command line scripts. +- [ ] Better documentation. +- [ ] More robust unittest. + +## Contribution + +- Comments or suggestions via github issues. +- Pull requests are welcome absolutely. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e76eb7 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# WxPusher + +[![PyPI version](https://badge.fury.io/py/wxpusher.svg)](https://badge.fury.io/py/wxpusher) +[![PyPI license](https://img.shields.io/pypi/l/wxpusher.svg)](https://pypi.python.org/pypi/wxpusher/) +[![Python Versions](https://img.shields.io/pypi/pyversions/wxpusher.svg)](https://pypi.python.org/pypi/wxpusher/) +[![Downloads](https://pepy.tech/badge/wxpusher)](https://pepy.tech/project/wxpusher) + +WxPusher Python SDK. + +*其他语言版本: [English](README-en.md), [简体中文](README.md).* + +## 入门指南 + +### 安装 + +```shell +pip install -U wxpusher +``` + +### 使用 + +```python +from wxpusher import WxPusher +WxPusher.send_message('', '', '') +WxPusher.query_message('') +WxPusher.create_qrcode('', '', '') +WxPusher.query_user('', '', '') +``` + +## 运行测试 + +### 配置 + +运行测试需要配置好 `appToken` 和 `uids`。 + +首先,将 `wxpusher/tests/` 文件夹下的配置样例 `config.sample.py` 复制并命名为 `config.py`。 + +```shell +cd python/wxpusher/tests +cp config.sample.py config.py +``` + +然后,填写 `config.py` 中的相应信息。 + +### 启动测试 + +配置好后就可以使用 `tox` 来运行测试了。 + +```shell +tox +``` + +或者直接使用 `nose` 也可以 + +```shell +nosetests +``` + +## TODO + +- [x] 基本架构并上传到 PyPI +- [x] 发送消息. +- [x] 查询消息. +- [x] 创建二维码. +- [x] 查询用户. +- [ ] 更完备的客户端验证. +- [ ] 命令行脚本. +- [ ] 更完善的文档. +- [ ] 更完备的单元测试. + +## 贡献 + +- 通过 Github Issues 提交评论或建议。 +- 直接提交 Pull Requests 必须没问题。 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..7ec1d6d --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.1.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..242b07a --- /dev/null +++ b/setup.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: setup.py +Author: huxuan +Email: i(at)huxuan.org +Description: Python packaging for wxpusher. +""" +from setuptools import setup + +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3 :: Only', + 'Topic :: Utilities' +] + +INSTALL_REQUIRES = [ + 'requests' +] + +DEV_REQUIRES = [ + 'pycodestyle', + 'pyflakes', + 'pylint' +] + +TEST_REQUIRES = [ + 'coverage', + 'nose' +] + +EXTRAS_REQUIRE = { + 'dev': DEV_REQUIRES, + 'test': TEST_REQUIRES +} + +DESCRIPTION = ( + 'WxPusher Python SDK.' +) + + +def readme(): + """Parse README for long_description.""" + with open('README.md') as fin: + return fin.read() + + +setup(name='wxpusher', + version=open('VERSION').read().strip(), + description=DESCRIPTION, + long_description=readme(), + long_description_content_type='text/markdown', + classifiers=CLASSIFIERS, + keywords='wxpusher wechat push-notification', + url='https://github.com/wxpusher/wxpusher-sdk-python', + author='Xuan (Sean) Hu', + author_email='i+wxpusher@huxuan.org', + license='Apache License 2.0', + packages=['wxpusher'], + install_requires=INSTALL_REQUIRES, + extras_require=EXTRAS_REQUIRE, + python_requires='>=3', + include_package_data=True) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..b3602e7 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +# tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py3 + +[testenv] +deps = + .[test] +commands = + nosetests --with-coverage --cover-erase --cover-package=wxpusher diff --git a/wxpusher/__init__.py b/wxpusher/__init__.py new file mode 100644 index 0000000..379f4a2 --- /dev/null +++ b/wxpusher/__init__.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: __init__.py +Author: huxuan +Email: i(at)huxuan.org +Description: init for WxPusher. +""" +from .wxpusher import WxPusher +from .exceptions import WxPusherException +from .exceptions import WxPusherNoneTokenException + +__all__ = [ + 'WxPusher', + 'WxPusherException', + 'WxPusherNoneTokenException' +] + +__version__ = open('VERSION').read().strip() diff --git a/wxpusher/exceptions.py b/wxpusher/exceptions.py new file mode 100644 index 0000000..ce60077 --- /dev/null +++ b/wxpusher/exceptions.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: exceptions.py +Author: huxuan +Email: i(at)huxuan.org +Description: WxPusher Exceptions. +""" + + +class WxPusherException(Exception): + """WxPusher specific base exception.""" + + +class WxPusherNoneTokenException(WxPusherException): + """Raised when both token and default token are None.""" diff --git a/wxpusher/tests/__init__.py b/wxpusher/tests/__init__.py new file mode 100644 index 0000000..779e65a --- /dev/null +++ b/wxpusher/tests/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: __init__.py +Author: huxuan +Email: i(at)huxuan.org +Description: Init for unittest. +""" +from . import exceptions + +try: + from . import config +except ImportError: + raise exceptions.WxPusherTestNoConfigException + +__all__ = ['config'] diff --git a/wxpusher/tests/config.sample.py b/wxpusher/tests/config.sample.py new file mode 100644 index 0000000..ba16879 --- /dev/null +++ b/wxpusher/tests/config.sample.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: config.sample.py +Author: huxuan +Email: i(at)huxuan.org +Description: Unittest configuration sample. +""" +# the `appToken` for test. +TOKEN = '' + +# the `uids` for test, note that it should be a list. +UIDS = [ + '' +] diff --git a/wxpusher/tests/exceptions.py b/wxpusher/tests/exceptions.py new file mode 100644 index 0000000..f4d1967 --- /dev/null +++ b/wxpusher/tests/exceptions.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: exceptions.py +Author: huxuan +Email: i(at)huxuan.org +Description: Custom exceptions for unittest. +""" +from wxpusher import WxPusherException + + +class WxPusherTestNoConfigException(WxPusherException): + """Raised when no unittest configuration exists.""" diff --git a/wxpusher/tests/test_create_qrcode.py b/wxpusher/tests/test_create_qrcode.py new file mode 100644 index 0000000..4bbda5d --- /dev/null +++ b/wxpusher/tests/test_create_qrcode.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: test_create_qrcode.py +Author: huxuan +Email: i(at)huxuan.org +Description: Unittest for creating qrcode. +""" +import unittest + +from wxpusher import WxPusher + +from . import config + + +class TestCreateQRCode(unittest.TestCase): + """Unittest for creating qrcode.""" + + @classmethod + def setUpClass(cls): + WxPusher.default_token = config.TOKEN + + def test_create_qrcode(self): + """Positive case for creating qrcode.""" + res = WxPusher.create_qrcode( + self.test_create_qrcode.__doc__ + ) + self.assertIsInstance(res, dict) + self.assertIn('code', res) + self.assertEqual(1000, res['code']) diff --git a/wxpusher/tests/test_query_message.py b/wxpusher/tests/test_query_message.py new file mode 100644 index 0000000..9ee9d84 --- /dev/null +++ b/wxpusher/tests/test_query_message.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: test_send_message.py +Author: huxuan +Email: i(at)huxuan.org +Description: Unittest for querying message. +""" +import unittest +import random + +from wxpusher import WxPusher + + +class TestQueryMessage(unittest.TestCase): + """Unittest for querying message.""" + + def test_query_message(self): + """Positive case for querying message.""" + res = WxPusher.query_message(random.randint(1, 999999)) + self.assertIsInstance(res, dict) + self.assertIn('code', res) + self.assertEqual(1000, res['code']) diff --git a/wxpusher/tests/test_query_user.py b/wxpusher/tests/test_query_user.py new file mode 100644 index 0000000..368a23a --- /dev/null +++ b/wxpusher/tests/test_query_user.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: test_send_message.py +Author: huxuan +Email: i(at)huxuan.org +Description: Unittest for querying user. +""" +import random +import unittest + +from wxpusher import WxPusher + +from . import config + + +class TestSendMessage(unittest.TestCase): + """Unittest for querying user.""" + + @classmethod + def setUpClass(cls): + WxPusher.default_token = config.TOKEN + + def test_query_user(self): + """Positive case for querying user.""" + res = WxPusher.query_user( + 1, random.randint(1, 99) + ) + self.assertIsInstance(res, dict) + self.assertIn('code', res) + self.assertEqual(1000, res['code']) diff --git a/wxpusher/tests/test_send_message.py b/wxpusher/tests/test_send_message.py new file mode 100644 index 0000000..f78b2af --- /dev/null +++ b/wxpusher/tests/test_send_message.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: test_send_message.py +Author: huxuan +Email: i(at)huxuan.org +Description: Unittest for sending message. +""" +import unittest + +from wxpusher import WxPusher + +from . import config + + +class TestSendMessage(unittest.TestCase): + """Unittest for sending message.""" + + @classmethod + def setUpClass(cls): + WxPusher.default_token = config.TOKEN + + def test_send_message(self): + """Positive case for sending message.""" + res = WxPusher.send_message( + self.test_send_message.__doc__, + config.UIDS, + url='http://example.com/' + ) + self.assertIsInstance(res, dict) + self.assertIn('code', res) + self.assertEqual(1000, res['code']) diff --git a/wxpusher/wxpusher.py b/wxpusher/wxpusher.py new file mode 100644 index 0000000..4a879e8 --- /dev/null +++ b/wxpusher/wxpusher.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +File: wxpusher.py +Author: huxuan +Email: i(at)huxuan.org +Description: WxPusher Python SDK. +""" +import requests + +from . import exceptions + +BASEURL = 'http://wxpusher.zjiecode.com/api' + + +class WxPusher(): + """WxPusher Python SDK.""" + default_token = None + + @classmethod + def send_message(cls, content, uids, token=None, **kwargs): + """Send Message.""" + payload = { + 'appToken': cls.get_token(token), + 'content': content, + 'contentType': kwargs.get('content_type', 1), + 'topicIds': kwargs.get('topic_ids', []), + 'uids': uids, + 'url': kwargs.get('url') + } + url = f'{BASEURL}/send/message' + return requests.post(url, json=payload).json() + + @classmethod + def query_message(cls, message_id): + """Query message status.""" + url = f'{BASEURL}/send/query/{message_id}' + return requests.get(url).json() + + @classmethod + def create_qrcode(cls, extra, valid_time=1800, token=None): + """Create qrcode with extra callback information.""" + payload = { + 'appToken': cls.get_token(token), + 'extra': extra, + 'validTime': valid_time + } + url = f'{BASEURL}/fun/create/qrcode' + return requests.post(url, json=payload).json() + + @classmethod + def query_user(cls, page, page_size, token=None): + """Query users.""" + payload = { + 'appToken': cls.get_token(token), + 'page': page, + 'pageSize': page_size + } + url = f'{BASEURL}/fun/wxuser' + return requests.get(url, params=payload).json() + + @classmethod + def get_token(cls, token=None): + """Get token with validation.""" + token = token or cls.default_token + if not token: + raise exceptions.WxPusherNoneTokenException() + return token