From 65f706f0ead42b84f17129ad92c3accfdecb0879 Mon Sep 17 00:00:00 2001 From: Olivier Demah Date: Wed, 10 Dec 2014 22:33:51 +0100 Subject: [PATCH] first shoot --- MANIFEST.in | 1 + README.rst | 27 +++++++ requirements.txt | 2 + setup.py | 37 ++++++++++ wallabag_api/__init__.py | 2 + wallabag_api/wallabag.py | 135 ++++++++++++++++++++++++++++++++++ wallabag_api/wallabag_test.py | 44 +++++++++++ wallabag_mock.py | 62 ++++++++++++++++ 8 files changed, 310 insertions(+) create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 wallabag_api/__init__.py create mode 100644 wallabag_api/wallabag.py create mode 100644 wallabag_api/wallabag_test.py create mode 100644 wallabag_mock.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..dff08a6 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include *.txt *.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..4b22764 --- /dev/null +++ b/README.rst @@ -0,0 +1,27 @@ +============ +Wallabag API +============ + +Python API for Wallabagap + +Requirements : +============== +* Flask == 0.10.1 +* requests == 2.5.0 + + +Installation: +============= +to get the project, from your virtualenv, do : + +.. code:: python + + git clone https://github.com/foxmask/wallabag-api/ + +TODO : +====== + +Wait the final release of http://v2.wallabag.org to be able to use the REST API completly +this final release should be able to provide an oauth with the exchange of the token + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fd501f2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask==0.10.1 +requests==2.5.0 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0eb6281 --- /dev/null +++ b/setup.py @@ -0,0 +1,37 @@ +from setuptools import setup, find_packages +from wallabag_api import __version__ as version +import os + + +def strip_comments(l): + return l.split('#', 1)[0].strip() + + +def reqs(*f): + return list(filter(None, [strip_comments(l) for l in open( + os.path.join(os.getcwd(), *f)).readlines()])) + +install_requires = reqs('requirements.txt') + +setup( + name='wallabag_api', + version=version, + description='Wallabag API', + author='Olivier Demah', + author_email='olivier@foxmask.info', + url='https://github.com/foxmask/wallabag_api', + download_url="https://github.com/foxmask/wallabag_api/archive/wallabag_api-" + + version + ".zip", + packages=find_packages(), + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + ], + install_requires=install_requires, + include_package_data=True, +) diff --git a/wallabag_api/__init__.py b/wallabag_api/__init__.py new file mode 100644 index 0000000..a38d425 --- /dev/null +++ b/wallabag_api/__init__.py @@ -0,0 +1,2 @@ +VERSION = (0, 0, 1) # PEP 386 +__version__ = ".".join([str(x) for x in VERSION]) \ No newline at end of file diff --git a/wallabag_api/wallabag.py b/wallabag_api/wallabag.py new file mode 100644 index 0000000..e136007 --- /dev/null +++ b/wallabag_api/wallabag.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +__author__ = 'foxmask' + +import requests +import logging + +logging.basicConfig(format='%(message)s', level=logging.INFO) + +__all__ = ['Wallabag'] + + +class Wallabag(object): + """ + Python Class 'Wallabag' to deal with Wallabagap REST API + This class is able to handle any data from your Wallabagap account + """ + + host = '' + api_key = '' + user_agent = '' + + def __init__(self, host='http://v2.wallabag.org', api_key='', user_agent="WallabagPython"): + """ + init variable + :param host: string url to the official API Wallabagap + :param api_key: string of the key provided by Wallabagap + :param user_agent + """ + self.host = host + self.api_key = api_key + self.user_agent = user_agent + + def get_host(self): + """ + get the host from which to get API + :return host + """ + return self.host + + def query(self, url, params={}, method='get'): + """ + Do a query to the System API + :param url: url to the API + :param params: a dict with all the necessary things to query the API + :return json data + """ + #params = params + params['key'] = self.api_key + if method == 'get': + r = requests.get(self.get_host() + url, params=params) + elif method == 'post': + r = requests.post(self.get_host() + url, params=params) + elif method == 'patch': + r = requests.patch(self.get_host() + url, params=params) + elif method == 'delete': + r = requests.delete(self.get_host() + url, params=params) + return self.handle_json_response(r) + + + def handle_json_response(self, responses): + """ + get the json data response + :param responses: the json response + :return the json data without 'root' node + """ + if responses.status_code != 200: + raise Exception("Wrong status code: ", responses.status_code) + json_data = {} + try: + json_data = responses.json() + except: + for error in json_data['errors']: + logging.error("Wallabag: %s" % \ + json_data['errors'][error]['content']) + return json_data + + def get(self, token, user): + """ + List unread entries for the given user + :param token: the token that identified the user to access the API + :param user: the current user + :return json data + """ + return self.get_entries(token, user) + + def get_entries(self, token, user): + """ + List unread entries for the given user + :param token: the token that identified the user to access the API + :param user: the current user + :return json data + """ + return self.query('/api/u/{user}/entries.json'.format(user=user), {'token': token}, method="get") + + def get_entry(self, token, user, entry): + """ + Fetch an entry, regardless the status flags + :param user: entry of that user + :param entry: the entry + :return json data + """ + return self.query('/api/u/{user}/entry/{entry}'.format(user=user, entry=entry), + {'token': token}, method="get") + + def post_entries(self, token, user, url=[], tags=[]): + """ + Save a new entry for the given user + :param user: entry of that user + :param url: the url of the note to store + :param tags: the tags of the note to store if provided + :return json data + """ + params = {'token': token, 'url': url} + if len(tags) > 0: + params = {'token': token, 'url': url, 'tags': tags} + return self.query('/api/u/{user}/entries.json'.format(user=user), params, method="post") + + def patch_entries(self, token, user, entry=[]): + """ + Change several properties of an entry. I.E tags, archived, starred and deleted status + :param user: entry of that user + :param entry: the entry to 'patch' + :return json data + """ + params = {'token': token, 'entry': entry} + return self.query('/api/u/{user}/entries.json'.format(user=user), params, method="patch") + + def delete_entry(self, token, user, entry): + """ + Delete an entry + :param user: entry of that user + :param entry: the entry to 'delete' + :return json data + """ + return self.query('/api/u/{user}/entry/{entry}'.format(user=user, entry=entry), {'token': token}, method="delete") diff --git a/wallabag_api/wallabag_test.py b/wallabag_api/wallabag_test.py new file mode 100644 index 0000000..846e50c --- /dev/null +++ b/wallabag_api/wallabag_test.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +import unittest +from wallabag import Wallabag + + +class TestWallabag(unittest.TestCase): + + def setUp(self): + self.host = "http://localhost:5000" + self.api_key = '12334567890' + self.user = 'foxmask' + + def test_get(self): + w = Wallabag(self.host).get('ABCD', self.user) + self.assertIsInstance(w, dict) + + def test_get_entries(self): + w = Wallabag(self.host).get('ABCD', self.user) + self.assertIsInstance(w, dict) + + def test_get_entry(self): + w = Wallabag(self.host).get_entry('ABCD', self.user, 1) + self.assertTrue(w, str) + + def test_post_entries(self): + url = ['http://foobar.com/', 'http://barfoo.com/'] + tags = ['foo', 'bar'] + w = Wallabag(self.host).post_entries('ABCD', self.user, url, tags) + self.assertTrue(w, True) + + def test_patch_entries(self): + entry = [] + entry.append('fourth content') + entry.append('fifth content') + w = Wallabag(self.host).patch_entries('ABCD', self.user, entry) + self.assertTrue(w, True) + + def test_delete_entry(self): + entry = 1 + w = Wallabag(self.host).delete_entry('ABCD', self.user, entry) + self.assertTrue(w, True) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/wallabag_mock.py b/wallabag_mock.py new file mode 100644 index 0000000..c10ca03 --- /dev/null +++ b/wallabag_mock.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +__author__ = 'foxmask' +import json +from flask import Flask, request + +""" + The main purpose of this script is to replace v2.wallabag.org itself + just run : + python wallabag_mock.py & + and then you can launch + python wallabag_test.py + +""" + +app = Flask(__name__) + +@app.route('/api/u//entries.json', methods=['GET']) +def get(user): + my_data = dict() + if user == 'foxmask': + my_data['entry'] = 'first content' + my_data['entry'] = 'second content' + return json.dumps(my_data,encoding='utf-8') + +@app.route('/api/u//entries.json', methods=['GET']) +def get_entries(user): + my_data = dict() + if user == 'foxmask': + my_data['entry'] = 'first content' + my_data['entry'] = 'second content' + return json.dumps(my_data,encoding='utf-8') + +@app.route('/api/u//entry/', methods=['GET']) +def get_entry(user, entry): + my_data = dict() + if user == 'foxmask' and entry == 1: + my_data['entry'] = 'third content' + return json.dumps(my_data,encoding='utf-8') + +@app.route('/api/u//entries.json', methods=['POST']) +def post_entries(user): + if user == 'foxmask': + return json.dumps(True) + else: + return json.dumps(False) + +@app.route('/api/u//entries.json', methods=['PATCH']) +def patch_entries(user): + if user == 'foxmask': + return json.dumps(True) + else: + return json.dumps(False) + +@app.route('/api/u//entry/', methods=['DELETE']) +def delete_entry(user, entry): + if user == 'foxmask' and entry == 1: + return json.dumps(True) + else: + return json.dumps(False) + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file