diff --git a/README.md b/README.md new file mode 100644 index 0000000..36cac72 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# python-api fork + +Add some code to use the Catch.com REST API v3 using Basic HTTP Auth. diff --git a/catchapi/__init__.py b/catchapi/__init__.py index 7c03cf1..22a5e7f 100644 --- a/catchapi/__init__.py +++ b/catchapi/__init__.py @@ -19,6 +19,7 @@ import mimetypes, base64, httplib, urllib, os, sys, urlparse, datetime import simplejson as json +import base64 class User(dict): """ @@ -138,7 +139,7 @@ def __init__(self, user, session, *args, **kwds): self._session = session self._dirty = False super(Note, self).__init__(*args, **kwds) - self['media'] = (Media(self._user, self._session, self) for m in self['media']) + self['media'] = 'media' in self and (Media(self._user, self._session, self) for m in self['media']) or () @property def deleted(self): @@ -259,3 +260,54 @@ def login(self, username, password): @property def _user_agent(self): return ' '.join(("python", "catch.api-%s" % __version__)) + + ## + # Added by @NilsHamerlinck + ## + + # HTTP Basic Authentification + def get_headers_basicauth(self, username, password): + auth = base64.encodestring('%s:%s' % (username, password)).replace('\n', '') + headers = { "Authorization": "Basic %s" % auth} + return headers + + # note as a simple dict + def move_note(self, note, stream_id, username, password): + params = { + "server_modified_at": note["server_modified_at"], + } + + data = self._request("PUT", + "/v3/streams/%s/%s" % (stream_id, note['id']), + body=params, + headers=self.get_headers_basicauth(username, password)) + + if data["status"] == "ok": + + # remove from the default stream + + data = self._request("DELETE", + "/v3/streams/default/%s" % note['id'], + body=params, + headers=self.get_headers_basicauth(username, password)) + + if data["status"] == "ok": + return True + + return False + + def post_note_v3(self, text, username, password, stream_id="default", **kwds): + params = { + "type": "note", + "text": text, + "streams": stream_id + } + params.update(kwds) + + data = self._request("POST", + "/v3/streams/sync", + body=params, + headers=self.get_headers_basicauth(username, password)) + + #print data + return data['result'] diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..bf9c780 --- /dev/null +++ b/cli.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# @author NilsHamerlinck +# +# CLI to consult or manage Catch.com notes from your shell +# +# Uses Catch.com API v3 +# +# Use this to export all your notes : +# $ curl -u username:password -X GET "https://api.catch.com/v3/streams/sync?full=true&limit=0" > export_v3.json +# +# Tags must be escaped: +# $ python cli.py search \#work + +import codecs, locale, os, sys + +sys.stdout = codecs.getwriter('utf-8')(sys.stdout) +#sys.stderr = codecs.getwriter('utf-8')(sys.stderr) + +import simplejson as json +from datetime import datetime + +DEFAULT_EXPORT_FILE = '../../data/export_v3.json' +DEBUG = False +VERBOSE = True + +import catchapi + +def change_stream_v3(tag, stream_id, username, password): + api = catchapi.CatchSession() + + ok = 0 + fail = 0 + + f = json.load(open(DEFAULT_EXPORT_FILE)) + for note in f["result"]["objects"]: + if tag in note["tags"] and "default" in note["streams"]: + if VERBOSE: + print "moving %s to %s:" % (note["id"], stream_id), + if DEBUG: + continue + + if api.move_note(note, stream_id, username, password): + ok += 1 + if VERBOSE: + print "OK" + else: + fail += 1 + if VERBOSE: + print "FAIL" + + print "Done (ok: %d, fail: %d)" % (ok, fail) + +def search(terms): + s = ' '.join(terms) + + f = json.load(open(DEFAULT_EXPORT_FILE)) + print 'Looking for "%s" in %d notes' % (s, f["result"]["count"]) + print '-------------------------' + for note in f["result"]["objects"]: + if note["text"] and s in note["text"]: + d = datetime.strptime(note["modified_at"], '%Y-%m-%dT%H:%M:%S.%fZ').strftime('%Y/%m/%d %H:%M:%S') + print '%s (%s):' % (d, note["id"]) + print note["text"] + print '--' + +def main(): + global DEBUG + global VERBOSE + + import sys + from optparse import OptionParser + + usage=""" +%prog [ options ] + +Examples: + +$ python cli.py search toto + +Search for notes containing toto + +$ python clip.py move tag 1234 --username=username + +To move notes tagged #toto from "default" to the stream whose id is 1234 + + """[1:-3] + + parser = OptionParser(usage=usage) + parser.add_option('--debug', + help='ne rien faire vraiment', + default=DEBUG, + action='store_true', + dest='debug') + parser.add_option('--verbose', + help='verbose', + default=VERBOSE, + action='store_true', + dest='verbose') + parser.add_option('--username', + help='username', + default=None, + action='store', + dest='username') + + + options, args = parser.parse_args() + + DEBUG = options.debug + VERBOSE = options.verbose + + if len(args) > 0: + if args[0] == 'move': + if not options.username: + print usage + return -1 + + from getpass import getpass + password = getpass() + + change_stream_v3(args[1], args[2], options.username, password) + elif args[0] == 'search': + return search(args[1:]) + else: + print usage + else: + print usage + + return 0 + +if __name__ == '__main__': + sys.exit(main())